zoukankan      html  css  js  c++  java
  • Python_基础_(面向对象进阶)

    一,isinstance(obj,cls)  issubclass(sub,super)

    isinstance(obj,cls)  # 判断对象obj是否是cls的一个实例

    class Test:
        pass
    t = Test()
    print(isinstance(t,Test))   # True

    issubclass(sub,super)  # 判断类sub是否为类super的子类

    class Test:
        pass
    class Sub(Test):
        pass
    print(issubclass(Sub,Test))     # True
    s = Sub()
    print(isinstance(s,Sub))        # True
    print(isinstance(s,Test))       # True

    type(f)  # 可以用来看对象f使用哪个类产生的

    class Foo:
        pass
    f = Foo()
    print(type(f))
    # <class '__main__.Foo'>

    ....

    二,__getattribute__

    ## 当 __getattr__和 __getattribute__同时出现时

    class Test:
        def __init__(self,name):
            self.name = name
        
        def __getattr__(self,item):
            print("执行的是__getattr__")
            
        def __getattribute__(self,item):
            print("执行的是__getattribute__")
            
    t = Test("henry")
    t.name     # 当调用存在的属性:触发 __getattribute__  不触发 __getattr
    t.xxxxxx# 当调用不存在的属性:触发 __getattribute__

    ## __getattribute__ 抛出异常时

    class Test:
        def __init__(self,name):
            self.name = name
        
        def __getattr(self,item):
            print("执行的是__getattr__")
            
        def __getattribute__(self,item):
            print("执行的是__getattribute__")
            raise AttributeError("抛出一个属性异常")
    t = Test("henry")
    t.xxxxxx    # 执行 __getattribute__时会抛出一个属性异常,不会直接报错,而后会去执行 __getattr__
    ## 输出
    执行的是__getattribute__
    执行的是__getattr__
    # 注:当抛出的异常不是AttributeError,不会去执行 __getattr__ ,而是直接终止程序

    ...

    三,__setitem__  __getitem__  __delitem__

    # 字典形式的操作属性就是和item相关
    # 点的形式操作属性,就是和内置方法相关

    class Test:
        
        def __setitem__(self,key,value):
            parint("__sttitem__")
            self.__dic__[key] = value
        
        def __getitem__(self,item):
            parint("__getitem__")
            
        def __delitem__(self,key):
            print("__delitem__")
            self.__dic__.pop(key)
    ## 添加
    t = Test()
    t["name"] = "henry"    # 添加一个属性和值,会触发 __setitem__
    t["user"] = "alex"
    print(t.__dic__)    # {"name":"henry"}
    
    ## 删除
    del t.name            # 不会触发item操作 只是普通的操作
    print(t.__dic__)    # {}
    
    del t["name"]        # 会触发__delitem__
    print(t.__dic__)    # {}
    
    ## 获取
    t.user                # 不会触发getitem操作
    t["user"]            # 会触发__getitem__

    ...

    四,__str__  __repr__

    # 当打印一个实例化的对象时

    class Test:
        pass
    t = Test()
    print(t)    # <__main__.Test object at 0x00000299CE10EBA8>

    # __str__ 可以控制打印实例化的对象时的信息

    class Test:
        def __str__(self):
            return "我是实例化的对象打印的信息"
    t = Test()
    print(t)    # 我是实例化的对象打印的信息

    # __repr__ 也是用于控制输出,应用于解释器中(cmd)

    class Test:
        def __repr__(self):
            return "我是实例化的对象打印的信息"
    t = Test()
    print(t)    # 我是实例化的对象打印的信息

    # 注意

    ## 两者共存时,优先选择str
    ## 如果__str__没有被定义,那么就会用__repr__来替代输出
    ## str 和 repr 输出的结果返回的值都为字符串类型

    class Test:
        def __str__(self):
            return "自定义的对象显示方式str"
        def __repr__(self):
            return "自定义的对象显示方式repr"
    t = Test()
    print(t)    # 输出:自定义的对象显示方式str

     ...

    五,自定义格式化 __format__

    class Test:
        def __init__(self,year,mon,day):
            self.year = year
            self.mon = mon
            self.day = day
    t = Test(2018,12,22)
    x = "{0.year},{0.mon},{0.day}".format(t)
    y = "{0.year}:{0.mon}:{0.day}".format(t)
    z = "{0.year}-{0.mon}-{0.day}".format(t)
    print(x)    # 2018,12,22
    print(y)    # 2018:12:22
    print(z)    # 2018-12-22

    # 当上方的程序要换一种日期输出格式时,必须得用户来定义,这必然是不行的

    format_dic = {
        "ymd":"{0.year},{0.mon},{0.day}",
        "y:m:d":"{0.year}:{0.mon}:{0.day}",
        "y-m-d":"{0.year}-{0.mon}-{0.day}"
    }
    class Test:
        def __init__(self, year, mon, day):
            self.year = year
            self.mon = mon
            self.day = day
        def __format__(self, format_spec):
            if not format_spec or format_spec not in format_dic:
                # 当用户没有输入格式时,程序自己选择为ymd
                # 当用户输入的格式不存在时
                format_spec = "ymd"
            fm = format_dic[format_spec]
            return fm.format(self)  # 相当于"{0,year}-{0,mon}-{0,day}",format(t)
    
    t = Test(2018, 12, 22)
    format(t)  # 相当于执行了 t.__format__()
    print(format(t, "ymd"))     # 2018,12,22
    print(format(t, "y:m:d"))   # 2018:12:22
    print(format(t, "y-m-d"))   # 2018-12-22

    ...

    六,__slots__

    1:__slots__:是一个变量,变量的值可以是列表,元组,字符串,可迭代的对象
    2:当用点来访问一个属性时,实际上是在访问类或者对象的__dict__属性字典(类的属性字典是共享的,可被每个实例对象访问;二实例的字典是独立的)
    3:为何使用了__slots__:因为字典会占用大量的内存,使用了__slots__不能再给其添加属性,只能使用之前在__slots__中定义的
    4:定义了__slots__后不在支持一些普通类的特性,比如继承

    # 基本形式

    class Test:
        __slots__ = "name"
        # __slots__ = ['name','age']
        
    t = Test()
    t.name = "henry"
    t.age = 18            
    # 报错,当前 __slots__替代了 __dic__,不能添加该属性
    # 限制创建属性
    print(t.__slots__)        # name
    print(Test.__slots__)    # name

     ...

    七,__doc__  __module__  __class__

    # __doc__

    # 注:该属性不能被继承
    class Test:
        '我是文档注释'
        pass
        
    pring(Test.__doc__)
    
    # 不能被继承
    class Test:
        '我是文档注释'
        pass
    class Foo(Test):
        pass
    print(Foo.__doc__)    # 无法继承给子类

    # __module__    __class__    

    # test1.py位于文件 lib下
    class A:
        def __init__(self):
            self.name = name
    
    # 在test2.py文件中导入 test1.py模块
    from lib.test1 import A
    
    obj = A()
    print(obj.__module__)    # 输出lib.test1        # 输出模块
    print(obj.__class__)    # 输出 lib.test1.A    # 输出类

    ...

    八,__del__析构方法

     1:当对象在内存中被释放时,自动触发执行

    2:在Python中程序员无需关心内存的分配和释放,Python解释器会自动来执行,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的,该方法一般无需定义

    # 当删除一个对象时,触发__del__

    class Test:
        def __del__(self):
            print("执行了del方法")
    t = Test()
    del t        # 删除对象
    print("程序执行完毕")
    # 输出
    执行了del方法
    程序执行完毕

    # 当程序执行完毕后,触发__del__

    class Test:
        def __del__(self):
            print("执行了del方法")
    t = Test()
    print("程序执行完毕")
    # 输出
    程序执行完毕
    执行了del方法
    # 在程序执行完成后,会执行垃圾回收机制

    # 当删除一个属性时,不会触发__del__

    # 当删除一个属性时,不会执行__del__方法
    class Test:
        def __init__(self,name):
            self.name = name
        def __del__(self):
            print("执行了del方法")
    t = Test("henry")
    del t.name
    print("程序执行完毕")
    # 输出
    程序执行完毕
    执行了del方法

    ...

    九,__call__

    class Test:
        def __call__(self, *args, **kwargs):
            print("执行了__call__")
    
    t = Test()
    t()     # 一个对象后加括号,触发执行__call__

    # 一切皆对象,假如Test类由类Foo产生,则执行Test(),相当于执行了类Foo下的__call__方法

    十,__next__  __iter__

     ## 当对一个被“工厂方法”list进行实例化的字符串"hello",进行for循环

    l_num = list("hello")
    for i in l_num:
        print(i)
    # h
    # e
    # l
    # l
    # o

    ## 因为上方是实例化得到的是一个对象,而类进行实例化得到的也是一个对象,想必也可进行for循环

    class Test:
        pass
    t = Test()
    for i in t:
        print(i)
    # 报错:TypeError: 'Test' object is not iterable
    # 不可进行迭代

    ## 在一个类中实现添加内置方法__iter__ 和 __next__实现迭代器协议

    class Test:
        def __init__(self,n):
            self.n = n
    
        def __iter__(self):     # 迭代器协议之一
            return self
    
        def __next__(self):     # 迭代器协议之二
            if self.n == 20:
                raise StopIteration("程序终止了")
            self.n += 1
            return self.n
    t = Test(10)
    print(t.__next__())     # 11
    print(t.__next__())     # 12
    print(t.__next__())     # 13
    print(t.__next__())     # 14
    print(next(t))          # 15
    print(next(t))          # 16
    print(next(t))          # 17
    print(next(t))          # 18
    print(next(t))          # 19
    print(next(t))          # 20   抛出异常StopIteration

    ## 用迭代器协议实现斐波那契数列

    # 斐波那契数列  前两个的和等于第三个的值
    class Fib:
        def __init__(self):
            self.a = 1
            self.b = 1
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.a > 100:
                raise StopIteration("程序终止了")
            self.a,self.b = self.b,self.a + self.b
            return self.a
    f = Fib()
    for i in f:
        print(i)
    1
    2
    3
    5
    8
    13
    21
    34
    55
    89
    144
    示例代码

    十一,描述符的基本概况

     # 描述符:本质就是在一个新式类中,这个新式类中至少实现 __get__(),__set__(),__delete__()中的一个,这三个也被称为描述符协议

    __get__() # 调用一个属性时触发

    __set__() # 给一个属性赋值时触发

    __delete__() # 利用del删除属性时触发

    # 什么情况下会触发着三个方法(以下为错误示范)

    class Test:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def __set__(self, instance, value):
            print("__set__")
    
        def __get__(self, instance, owner):
            print("__get__")
    
        def __delete__(self, instance):
            print("__delete__")
    
    t = Test("henry",18)
    t.name          # 没调用
    t.name = "alex" # 没调用
    del t.name      # 没调用

    # 什么情况下会触发这三个方法(以下为正确示范)

    class Test:
        def __get__(self, instance, owner):
            print("触发__get__")
        def __set__(self, instance, value):
            print("触发__set__")
        def __delete__(self, instance):
            print("触发__delete__")
    
    class Bar:
        name = Test()
        # 数据描述符
        # 表示一个描述符 所有与name有关的操作都去找类Test
        # 表示在类Bar中利用类Test来描述name属性
        def __init__(self,name):    # name被Test()代理
            self.name = name
    
    b = Bar("henry")        # 触发__set__,instance为b,value为henry
    b.name                  # 触发__get__
    b.name = "heihei"       # 触发__set__
    del b.name              # 触发__delete__
    
    
    print(b.__dict__)       # {}
    print(Bar.__dict__)     # 'name': <__main__.Test object at 0x0000017E78719470>
    class Test_1:
        def __get__(self, instance, owner):
            print("Test_1触发__get__")
        def __set__(self, instance, value):
            print("Test_1触发__set__")
        def __delete__(self, instance):
            print("Test_1触发__delete__")
    class Test_2:
        def __get__(self, instance, owner):
            print("Test_2触发__get__")
        def __set__(self, instance, value):
            print("Test_2触发__set__")
        def __delete__(self, instance):
            print("Test_2触发__delete__")
    
    class Bar:
        name = Test_1()
        age = Test_2()
        def __init__(self,name,age):    # name被Test_1()代理,age被Test_2()代理
            self.name = name
            self.age = age
    
    b = Bar("henry",18)
    # Test_1触发__set__
    # Test_2触发__set__
    
    
    # Test_1
    b.name
    b.name = "heihei"
    del b.name
    # Test_1触发__get__
    # Test_1触发__set__
    # Test_1触发__delete__
    
    # Test_2
    b.age
    b.age = 19
    del b.age
    # Test_2触发__get__
    # Test_2触发__set__
    # Test_2触发__delete__
    
    
    print(b.__dict__)       # {}
    print(Bar.__dict__)
    # 'name': <__main__.Test_1 object at 0x0000021EF9F7F780>, 
    # 'age': <__main__.Test_2 object at 0x0000021EF9F7FCC0>, 
    相同的一种示范

    # 数据描述符和非数据描述符

    # 数据描述符
    class Foo:
        def __set__(self, instance, value):
           print('set')    
        def __get__(self, instance, owner):
           print('get')
    
    # 二 非数据描述符:没有实现__set__()
    class Foo:
        def __get__(self, instance, owner):
            print('get')
    示例代码

    # 描述符的注意事项

    一:描述符不本身应该定义成新式类,被代理的类也应该时形式类

    二:必须把描述符定义成则个类的类属性,不能为定义到构造函数中

    三:要严格遵循优先级

    1,类属性

    2,数据描述符     # 具有 __get__()和__set__()

    3,实例属性

    4,非数据描述符   # 没有__set__()

    5,找不到属性触发 __getattr__()

    # 描述符优先级问题

    ## 类属性>数据描述符

    class Test:
        def __get__(self, instance, owner):
            print("调用get")
        def __set__(self, instance, value):
            print("调用set")
        def __delete__(self, instance):
            print("调用delete")
    
    class Foo:
        name = Test()
        def __init__(self,name):
            self.name = name
    
    Foo.name            # # 输出:调用get
    # 在Foo的属性字典中,'name': <__main__.Test object at 0x00000262B7BF97B8>,
    # 当调用属性name时,属性name为描述符伪装成类属性name,当在类中找不到属性对饮的值,就到类Test中,触发了__get__()
    
    Foo.name = "henry"
    # 直接赋值类的一个属性,因为类属性拥有更高的优先级,相当于覆盖了描述符,不会触发描述符中的__set__()
    
    print(Foo.__dict__)     # "name":"henry"
    del Foo.name            # 删除的为类中的类属性,没有触发__delete__()
    示例代码

    ## 数据描述符>实例属性

    class Test:
        def __get__(self, instance, owner):
            print("调用get")
        def __set__(self, instance, value):
            print("调用set")
        def __delete__(self, instance):
            print("调用delete")
    
    class Foo:
        name = Test()
        def __init__(self,name):
            self.name = name
    
    f = Foo("henry")
    print(f.__dict__)   # {}
    # 实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除/都和实例无关
    
    f.name = "heihei"   # 输出:调用set
    # 先在自己的实例属性中找,找不到则到类中找
    # f.name的调用与赋值都是触发描述符的操作,与f本身无法无关,相当于覆盖了实例属性
    
    del f.name  # 调用delete
    示例代码

    ## 实例属性>非数据描述符

    class Test:
        def __get__(self, instance, owner):
            print("调用get")
        # def __delete__(self, instance):
        #     print("调用delete")
    
    class Foo:
        name = Test()       # 变成了非数据描述符
    
    
    # name是一个非数据描述符,因为name=Test() 而Test()没有实现set方法,因而比实例属性由更低的优先级
    f = Foo()
    f.name  # 调用get
    
    f.name = "henry"        # 相当于在类Foo中进行设置属性的值
    print(f.__dict__)       # {'name': 'henry'}
    # 实例属性的优先级高于非数据描述符,当描述符中没有set时,就在自己的实例上添加属性
    示例代码

    ...

    十二,描述符的应用

    # 描述符中的参数

    class Test:
        def __get__(self, instance, owner):
            print("执行get")
            print("instance参数为:%s" %instance)
            print("owner参数为:%s" % owner)
        def __set__(self, instance, value):
            print("执行set")
            print("instance参数为:%s" % instance)
            print("value参数为:%s" % value)
        def __delete__(self, instance):
            print("执行delete")
            print("instance参数为:%s" % instance)
    
    class Foo:
        name = Test()
        def __init__(self,name,age,salary):
            self.name = name
            self.age = age
            self.salary = salary
    
    f = Foo("henry",18,10.1)
    # 执行set
    # instance参数为:<__main__.Foo object at 0x000001F1E436E748>
    # value参数为:henry
    
    f.name
    # 执行get
    # instance参数为:<__main__.Foo object at 0x000001F1E436E748>
    # owner参数为:<class '__main__.Foo'>
    
    print(f.__dict__)
    # name不存在对象f的字典中
    # {'age': 18, 'salary': 10.1}
    示例代码

    # 简单操作

    class Test:
        def __init__(self,key):
            self.key = key
    
        def __get__(self, instance, owner):
            print("执行get")
            return instance.__dict__[self.key]     # instance为对象f,owner为类Foo
    
        def __set__(self, instance, value):
            print("执行set")
            instance.__dict__[self.key] = value        # 相当于给对象f添加键值对
    
        def __delete__(self, instance):
            print("执行delete")
            instance.__dict__.pop(self.key)
    
    class Foo:
        name = Test("name")
        def __init__(self,name,age,salary):
            self.name = name
            self.age = age
            self.salary = salary
    
    f = Foo("henry",18,10.1)
    print(f.name)       # henry
    print(f.__dict__)   # {'key': 'henry', 'age': 18, 'salary': 10.1}
    f.name = "heihei"
    print(f.__dict__)   # {'key': 'heihei', 'age': 18, 'salary': 10.1}
    del f.name
    print(f.__dict__)   # {'age': 18, 'salary': 10.1}
    示例代码

    # 针对name赋值时的类型检查

    class Test:
        def __init__(self,key):
            self.key = key
    
        def __get__(self, instance, owner):
            print("执行get")
            return instance.__dict__[self.key]     # instance为对象f,owner为类Foo
    
        def __set__(self, instance, value):
            print("执行set")
            if not type(value) == str:  # 判断类型
                raise TypeError("你传入的不是字符串类型")
            instance.__dict__[self.key] = value
    
        def __delete__(self, instance):
            print("执行delete")
            instance.__dict__.pop(self.key)
    
    class Foo:
        name = Test("name")
        def __init__(self,name,age,salary):
            self.name = name
            self.age = age
            self.salary = salary
    
    f = Foo(123,18,10.1)
    执行set
    Traceback (most recent call last):
      File "F:/Python_Project/Test/描述符的应用.py", line 26, in <module>
        f = Foo(123,18,10.1)
      File "F:/Python_Project/Test/描述符的应用.py", line 22, in __init__
        self.name = name
      File "F:/Python_Project/Test/描述符的应用.py", line 12, in __set__
        raise TypeError("你传入的不是字符串类型")
    TypeError: 你传入的不是字符串类型
    示例代码

    # 针对name和age赋值时的类型检查

    class Test:
        def __init__(self,key,expected_type):   # expected_type想要检测的类型
            self.key = key
            self.expected_type = expected_type
    
        def __get__(self, instance, owner):
            print("执行get")
            return instance.__dict__[self.key]     # instance为对象f,owner为类Foo
    
        def __set__(self, instance, value):
            print("执行set")
            if not type(value) == self.expected_type:  # 判断类型
                raise TypeError("你传入的不是%s类型" %self.expected_type)
            instance.__dict__[self.key] = value
    
        def __delete__(self, instance):
            print("执行delete")
            instance.__dict__.pop(self.key)
    
    class Foo:
        name = Test("name",str)
        age = Test("age",int)
        def __init__(self,name,age,salary):
            self.name = name
            self.age = age
            self.salary = salary
    
    # f = Foo(123,18,10.1)    # TypeError: 你传入的不是<class 'str'>类型
    f = Foo("henry","18",10.1)  # TypeError: 你传入的不是<class 'int'>类型
    示例代码

    ...

    十三,__enter__  __exit__   上下文管理协议

    ## 第一种文件操作(使用完得关闭文件)
    f = open("xxx","r")
    文件操作
    f.close
    
    ## 另一种文件操作(使用完不必关闭文件)
    with open("xxxx","r") as file
        "文件操作代码块"

    # 上述称为上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

    ## 上下文管理协议

    # 一 数据描述符:至少实现了__get__()和__set__()
    class Open:
        def __init__(self,name):
            self.name = name
    
        def __enter__(self):        # 在with Open("a.txt") as f:执行时触发
            print("执行了enter")
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):  # 等到代码块执行完成后执行
            print("执行了exit")
            return self
    
    with Open("a.txt") as f:
        print("代码块执行完成")
    # 输出
    执行了enter
    代码块执行完成
    执行了exit
    
    # Open("a.txt")的过程创建了一个对象,当执行with时执行了__enter__(),__enter__()返回一个值(self)赋值给f,f就是类Open产生的一个对象

    ## 当with中的代码块出现异常时

    class Open:
        def __init__(self,name):
            self.name = name
    
        def __enter__(self):        # 在with Open("a.txt") as f:执行时触发
            print("执行了enter")
    
        def __exit__(self, exc_type, exc_val, exc_tb):  # 等到代码块执行完成后执行
            print("执行了exit")
            print(exc_type)
            print(exc_val)
            print(exc_tb)
            # return True        当在exit中返回一个True时,代表不抛出程序出现异常 
    
    with Open("a.txt") as f:
        print("代码块开始执行执行")
        print(abc)      # 当在with中出现一个错误时
        print("代码块执行完成")
    print("11111111111")

    # 当不return True时的 输出的结果
      执行了enter
      代码块开始执行执行
      执行了exit
      Traceback (most recent call last):
      <class 'NameError'>
        File "F:/Python_Project/Test/利用描述符定制property.py", line 17, in <module>
      name 'abc' is not defined
          print(abc)      # 当在with中出现一个错误时
      <traceback object at 0x000001E2A2D59FC8>
      NameError: name 'abc' is not defined

    # 当return True时的结果
    执行了enter
    代码块开始执行执行
    执行了exit
    <class 'NameError'>
    name 'abc' is not defined
    <traceback object at 0x000001E205DB9F88>
    11111111111

    ## exit中的三个参数

    exc_type:异常类
    exc_val:异常值
    exc_tb:异常追踪信息


    <class 'NameError'>
        +
    name 'abc' is not defined
        +
    <traceback object at 0x0000016F7FAE9FC8>
        =
    NameError: name 'abc' is not defined

    ## 总结

    with obj as f:
    "代码块"
    1:with obj # 触发obj.__enter__(),获得返回值self
    2:as f # 将__enter__返回值赋值给f,f = 返回值
    3:with obj as f # 等同于 f = obj.__enter__()
    4:执行代码块
      1:在没有发生异常的情况下,整个代码块执行完成后触发__exit__,exit中的三个参数都为None
      2:在有异常的情况下,从异常出现的位置直接触发__exit__
        a:如果__exit__的返回值为True,代表吞掉异常,不抛出异常,程序不报错
        b:如果__exit__的返回值不为True,表示抛出异常,程序报错
        c:__exit__的运行完毕,表示整个with语句的执行完毕

     ...

  • 相关阅读:
    jQuery的AJAX请求成功,但是跳转到error的解决方法
    leaflet中如何通过透明度控制layerGroup的显示隐藏
    pg_ctl: no database directory specified and environment variable PGDATA unset , centos 7 postgreSQL
    MyBatisPlus乐观锁: Parameter ‘MP_OPTLOCK_VERSION_ORIGINAL‘ not found. Available parameters are [
    mybatisplus自动填充踩坑
    Linux如何查找大文件或目录总结
    MyBatisPlus中updateById与updateAllColumnById方法区别
    java 正则表达式替换Spring @RequestMapping URL中的@PathVariable值
    swagger2 Illegal DefaultValue null for parameter type integer
    【MybatisPlus进阶学习(八)】SQL注入器
  • 原文地址:https://www.cnblogs.com/Doaoao/p/10166411.html
Copyright © 2011-2022 走看看