• python类的相关知识第二部分


    类的继承、多态、封装

    一、类的继承

    1、应用场景:

    类大部分功能相同,大类包含小类的情况

    例如:

    动物类

    共性:都要吃喝拉撒、都有头有脚

    特性:

    猫类、走了很轻,叫声特别,喜欢白天睡觉

    狗类、的叫声很大,晚上睡觉

    2、继承顺序

    在python2版本中多重继承有分两种继承循序

    (1)、一直往上找 、找到最高级的父类再重另外一个分支找,直到找到为止。

    (2)、一直往上找,找到最高级父类的下一层后就不找了。从另外一个分支找,另外一个分支没找到最后才找最高级的父类。

    再python3中只有第一种继承循序。

    3、子类调用父类的方法:

     略

    4、接口继承和归一化设计

    接口继承和归一化设计例子:

     1 import abc
     2 
     3 class Animal(metaclass=abc.ABCMeta):
     4     @abc.abstractclassmethod
     5     def shout(self): #定义抽象方法,无需实现功能
     6         pass
     7 
     8     @abc.abstractclassmethod
     9     def run(self):
    10         pass
    11 
    12     @abc.abstractclassmethod
    13     def jump(self):
    14         pass
    15 
    16 
    17 class Felid(Animal): 
    18  # 子类继承抽象类,但是必须定义shout、run、jump方法,否则实例化后就报错了。这个就是归一化设计。
    19     def __init__(self,name):
    20         self.name = name
    21 
    22     def shout(self):
    23         print("%s 在叫" % self.name)
    24 
    25     def run(self):
    26         print("%s 在跑" % self.name)
    27 
    28     def jump(self):
    29         print("%s 在跳" % self.name)
    30 
    31 tiger = Felid("老虎")
    32 tiger.shout()
    接口继承和归一化设计

    二、类的多态

    1、应用场景:

    不同的类有相同的方法,不同的对象调用相同的方法。多态是依附于继承存在的,有了继承才能有多态。

    例如:

    H2O分子:有三种形态体现

    (1)、水

    (2)、冰

    (3)、水蒸气

    2、代码实现例子

     1 class H2O:
     2     def __init__(self,name,temp):
     3         self.name = name
     4         self.temp=temp
     5 
     6     def who_am_i(self):
     7         if int(self.temp) <0:
     8             print("I am %s." % self.name)
     9         elif int(self.temp) >0 and int(self.temp) <100:
    10             print("I am %s" % self.name)
    11         else:
    12             print("I am %s" % self.name)
    13 
    14 class Ice(H2O):
    15     pass
    16 
    17 class Water(H2O):
    18     pass
    19 
    20 class Steam(H2O):
    21     pass
    22 
    23 
    24 s1 = Ice("",-1)
    25 s2 = Water("",10)
    26 s3 = Steam("空气",1000)
    27 
    28 def func(obj):
    29     obj.who_am_i()
    30 
    31 func(s1)
    32 func(s2)
    33 func(s3)
    多态的例子

    三、类的封装

    1、单下划线和双下划线的私有属性和方法

    2、反射

    实现反射的四个方法:

    hasattr(object,name),getattr(object,name,default=None),setattr(object,name,value),delattr(object,name)

    四种方法例子展示:

     1 class Foo:
     2     foo_name = "my foo test"
     3 
     4     def __init__(self,name,addr):
     5         self.name = name
     6         self.addr = addr
     7 
     8     def test1(self):
     9         print("test1")
    10 
    11     def test2(self):
    12         print("test2")
    13 
    14 # hasattr
    15 t1 = Foo("FooTest","beijing")
    16 print(hasattr(t1,"foo_name"))
    17 print(hasattr(t1,"test1"))
    18 # getattr
    19 print(getattr(t1,"foo_name"))
    20 func1 = getattr(t1,"test1")
    21 func1()
    22 print(getattr(t1,"test10","not found"))
    23 # setattr
    24 setattr(t1,"start_time","2014")
    25 print(t1.__dict__)
    26 #delattr
    27 delattr(t1,"start_time")
    28 print(t1.__dict__)
    29 
    30 #打印显示##
    31 True
    32 True
    33 my foo test
    34 test1
    35 not found
    36 {'name': 'FooTest', 'addr': 'beijing', 'start_time': '2014'}
    37 {'name': 'FooTest', 'addr': 'beijing'}
    反射四个方法使用例子

    好处:

    (1)、实现可插拔机制

     1 # a同学写的类
     2 class FtpClient:
     3     "A同学写的功能,但是没有完成"
     4     def __init__(self,addr):
     5         print("正在连接服务器%s " % addr)
     6         self.addr = addr
     7     
     8     
     9 # b同学要调用,并进行其他的工作
    10 f1 = FtpClient("192.168.1.1")
    11 if hasattr(f1,"get"):
    12     func_get = getattr(f1,"get")
    13     func_get
    14 else:
    15     print("功能还未完成,等待中。。")
    反射插拔式设计例子

     可以看出,一方没有完成的,完全不影响另一方的其他工作的进行。

    (2)、实现动态导入模块

    1 import importlib
    2 
    3 __import__("import_lib.mataclass")
    4 #importlib.import_module("import_lib.mataclass") 
    5 #两种效果一样,但是官方建议用下面一种。

    3、__getattr__,__setattr__,delattr__

    例子:

     1 class Foo:
     2     x =1
     3     def __init__(self,y):
     4         self.y = y
     5 
     6     def __getattr__(self, item):
     7         print("----> from getattr: 你找的属性不存在")
     8 
     9     def __setattr__(self, key, value):
    10         print("----> from setattr: ")
    11         # self.key = value # 这里这么写是一个无限递归
    12         self.__dict__[key]=value # __setattr__ 其实就是重写了__dict__,所以不是特殊需求,一般都不会去动这块。
    13 
    14     def __delattr__(self, item):
    15         print("----> from delattr: ")
    16         self.__dict__.pop(item)
    17 
    18 # 触发__getattr__
    19 f1 = Foo(10)
    20 f1.z # 只有在使用点调用属性且属性找不到的时候才触发__getattr__
    21 
    22 # 触发__setattr__
    23 f1.z = 3
    24 print(f1.__dict__) # 这里显示字典为空,是因为重写了__setattr__,凡是赋值操作都会触发他的运行,这里什么都没有写,所以是空
    25 # 触发__delattr__
    26 f1.a = 3#
    27 del f1.a
    28 print(f1.__dict__)
    演示例子

    4、二次加工标准类型

    (1)、包装

    包装:python为我们提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了继承、派生知识。

    例子:

     1 class List(list):
     2     def append(self, p_object):
     3         "派生自己的append方法:加上类型检测"
     4         if not isinstance(p_object,int):
     5             print("must be int")
     6             return
     7         super().append(p_object)
     8 
     9     def mid(self):
    10         "新增一个取列表中间值的方法"
    11         index = len(self)//2
    12         return self[index]
    13 # 只允许数值进行append操作
    14 L1 = List([1,2,3,4,5])
    15 print(L1)
    16 L1.append(10)
    17 print(L1)
    18 L1.append("11")
    19 print(L1)
    20 # 取中间值
    21 print(L1.mid())
    22 
    23 ##打印信息
    24 [1, 2, 3, 4, 5]
    25 [1, 2, 3, 4, 5, 10]
    26 must be int
    27 [1, 2, 3, 4, 5, 10]
    28 4
    包装例子

    (2)、授权

    授权:授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其他的则保持原样。授权的过程,即是所有更新的功能都是有心累的某部分来处理,但是已存在的功能就授权给对象的默认属性。

    实现授权的关键点就是覆盖__getattr__方法。

    例子:

     1 import time
     2 
     3 class FileHandle:
     4     def __init__(self,filename,mode,encoding):
     5         self.file=open(filename,mode=mode,encoding=encoding)
     6 
     7     def write(self,line):  #重写了write方法
     8         t = time.strftime("%Y-%m-%d %X")
     9         self.file.write("%s %s" % (t,line))
    10 
    11     def __getattr__(self, item):
    12         # print(self.file,item,type(item))
    13         return getattr(self.file,item)
    14 
    15 
    16 f1 = FileHandle("a.txt","w+","utf-8")
    17 f1.write("aaaaaaaa
    ")
    18 f1.write("bbbb
    ")
    19 f1.seek(0)   ##使用原来open类的seek方法
    20 print(f1.read())
    21 
    22 ##打印
    23 2017-05-04 14:58:48 aaaaaaaa
    24 2017-05-04 14:58:48 bbbb
    授权例子

    5、__setitem__,__getitem__,delitem__

     这三个方法和上面的__setattr__,__getattr__,delattr__差不多,区别就在于,attr是用点的方式去访问的,item的是用["key"]的方式访问的。

    例子:

     1 class Foo:
     2     def __init__(self,name):
     3         self.name = name
     4 
     5     def __getitem__(self, item):
     6         print("----> getitem: ")
     7         print(self.__dict__[item])
     8 
     9     def __setitem__(self, key, value):
    10         print("----> setitem: ")
    11         self.__dict__[key] = value
    12 
    13     def __delitem__(self, key):
    14         print("----> delitem: ")
    15         self.__dict__.pop(key)
    16 
    17     def __delattr__(self, item):
    18         print("----> delattr: ")
    19         self.__dict__.pop(item)
    20 
    21 f1 = Foo("aaa")
    22 f1['age'] = 18  # 触发setitem
    23 f1['name']  #触发 getitem
    24 print(f1.__dict__)
    25 del f1.name
    26 del f1['age']
    27 f1["name"] = "bbb"
    28 print(f1.__dict__)
    29 
    30 #打印
    31 ----> setitem: 
    32 ----> getitem: 
    33 aaa
    34 {'name': 'aaa', 'age': 18}
    35 ----> delattr: 
    36 ----> delitem: 
    37 ----> setitem: 
    38 {'name': 'bbb'}
    例子

    6、__str__,__repr__,__format__

     改变对象的字符串显示__str__,__repr__

    自定制格式化字符串__format__

    例子:

     1 format_dict = {
     2     'nat':'{obj.name}-{obj.addr}-{obj.type}',
     3     'tna':'{obj.type}:{obj.name}:{obj.addr}',
     4     'tan':'{obj.type}/{obj.addr}/{obj.name}',
     5 }
     6 
     7 class School:
     8     def __init__(self,name,addr,type):
     9         self.name = name
    10         self.addr = addr
    11         self.type = type
    12 
    13     def __repr__(self):
    14         return 'School(%s,%s)' % (self.name,self.addr)
    15 
    16     def __str__(self):
    17         return "(%s,%s)" %(self.name,self.addr)
    18 
    19     def __format__(self, format_spec):
    20         if not format_spec or format_spec not in format_dict:
    21             format_spec='nat'
    22         fmt = format_dict[format_spec]
    23         return fmt.format(obj=self)
    24 
    25 s1 = School("清华","beijing","public")
    26 print('from repr: ',repr(s1))
    27 print('from str: ',str(s1))
    28 print(s1)
    29 """
    30 str函数或者print函数--->obj.__str__()
    31 repr函数或者交互式解释器--->obj.__repr__()
    32 如果__str__没有定义,那么就会使用__repr__来代替输出
    33 注意:这两个方法的返回值必须是字符串,否则抛出异常
    34 """
    35 print(format(s1,'nat'))
    36 print(format(s1,'tna'))
    37 print(format(s1,'tan'))
    38 print(format(s1,'aaa'))
    39 
    40 ##打印
    41 from repr:  School(清华,beijing)
    42 from str:  (清华,beijing)
    43 (清华,beijing)
    44 清华-beijing-public
    45 public:清华:beijing
    46 public/beijing/清华
    47 清华-beijing-public
    例子

    7、__slots__

    最大的用途就是用来节省内存。

     例子:

     1 '''
     2 1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
     3 2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
     4 3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
     5 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
     6 字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给
     7 实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
     8 4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该
     9 只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
    10 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。           更多的是用来作为一个内存优化工具。
    11 
    12 '''
    13 class Foo:
    14     __slots__='x'
    15 
    16 
    17 f1=Foo()
    18 f1.x=1
    19 f1.y=2#报错
    20 print(f1.__slots__) #f1不再有__dict__
    21 
    22 class Bar:
    23     __slots__=['x','y']
    24     
    25 n=Bar()
    26 n.x,n.y=1,2
    27 n.z=3#报错
    28 
    29 __slots__使用
    例子

    8、__iter__,__next__

     例子:

    class Fib:
        def __init__(self):
            self._a = 0
            self._b = 1
    
        def __iter__(self):
            return self
    
        def __next__(self):
            self._a,self._b=self._b,self._a + self._b
            return self._a
    
    f1 = Fib()
    
    print(f1.__next__())
    print(next(f1))
    print(next(f1))
    
    for i in f1:
        if i > 100:
            break
        print('%s ' %i,end='')
    
    #打印
    1
    1
    2
    3 5 8 13 21 34 55 89 
    实现迭代器协议例子

    9、文件描述符__set__,__get__,__delete__

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

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

    __set__():为一个属性赋值时,触发

    __delete__():采用del删除属性时,触发

    (2)、描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的属性,不能定义到构造函数中)

    (3)、描述符分两种:数据描述符(至少实现了__get__()和__set__())、非数据描述符(没有实现__set__())

    (4)、注意事项:

         (4.1)、描述符本身应该定义成新式类,被代理的类也应该是新式类

         (4.2)、必须把描述符定义成这个类的类属性,不能定义在构造函数中

         (4.3)、要严格遵循该优先级,优先级由高到低分别是:

              (4.3.1)、类属性

              (4.3.2)、数据描述符

              (4.3.3)、实例属性

              (4.3.4)、非数据描述符

              (4.3.5)、找不到的属性触发__getattr__()

    (5)、描述符的使用

     1 class Str:
     2     "数据描述符"
     3     def __get__(self, instance, owner):
     4         print("-->instance: %s" % instance)
     5         print("-->owner: %s" % owner)
     6 
     7     def __set__(self, instance, value):
     8         print("-->instance: %s" % instance)
     9         print("-->value: %s" % value)
    10 
    11     def __delete__(self, instance):
    12         print("-->instance: %s" % instance)
    13 
    14 class People:
    15     name = Str()  #描述符
    16     def __init__(self,name,age,salary):
    17         self.name = name
    18         self.age = age
    19         self.salary = salary
    20 
    21 p1 = People("test1",22,1121.1) # 触发 __set__
    22 p1.name # 触发__get__
    23 print(p1.__dict__) #发现name属性没有。这里说明一个问题,是数据描述符优先级高于实例属性。
    24 
    25 ##打印
    26 -->instance: <__main__.People object at 0x000002CA13189978>
    27 -->value: test1
    28 -->instance: <__main__.People object at 0x000002CA13189978>
    29 -->owner: <class '__main__.People'>
    30 {'age': 22, 'salary': 1121.1}
    数据描述符优先级例子
     1 class Str:
     2     "数据描述符"
     3     def __init__(self,key):
     4         self.key = key
     5 
     6     def __get__(self, instance, owner):
     7         return instance.__dict__[self.key]
     8 
     9     def __set__(self, instance, value):
    10         if not isinstance(value,str):
    11             print("传入是数据不是字符串!")
    12             # raise TypeError("传入是数据不是字符串!")
    13             return
    14         instance.__dict__[self.key] = value
    15 
    16     def __delete__(self, instance):
    17         instance.__dict__.pop[self.key]
    18 
    19 class People:
    20     name = Str("name")  #数据描述符
    21     def __init__(self,name,age,salary):
    22         self.name = name
    23         self.age = age
    24         self.salary = salary
    25 
    26 p1 = People("test1",22,1121.1) # 触发 __set__
    27 p1.name # 触发__get__
    28 print(p1.__dict__) #发现name属性没有。这里说明一个问题,是数据描述符优先级高于实例属性。
    29 p2 = People(1111,22,232323)
    30 print(p2.__dict__)
    31 
    32 #打印
    33 {'name': 'test1', 'age': 22, 'salary': 1121.1}
    34 传入是数据不是字符串!
    35 {'age': 22, 'salary': 232323}
    赋值类型判断的描述符例子
     1 class Str:
     2     "数据描述符"
     3     def __init__(self,key,exspect_type):
     4         self.key = key
     5         self.exspect_type = exspect_type
     6 
     7     def __get__(self, instance, owner):
     8         return instance.__dict__[self.key]
     9 
    10     def __set__(self, instance, value):
    11         if not isinstance(value,self.exspect_type):
    12             print("传入是数据不是%s" % self.exspect_type)
    13             # raise TypeError("传入是数据不是字符串!")
    14             return
    15         instance.__dict__[self.key] = value
    16 
    17     def __delete__(self, instance):
    18         instance.__dict__.pop[self.key]
    19 
    20 class People:
    21     name = Str("name",str)  #数据描述符
    22     age = Str("name",int)  #数据描述符
    23     salary = Str("name",float)  #数据描述符
    24     def __init__(self,name,age,salary):
    25         self.name = name
    26         self.age = age
    27         self.salary = salary
    28 
    29 p1 = People("test1",22,1121.1) # 触发 __set__
    30 p1.name # 触发__get__
    31 print(p1.__dict__) #发现name属性没有。这里说明一个问题,是数据描述符优先级高于实例属性。
    32 p2 = People("test2",22,232323)
    33 print(p2.__dict__)
    34 
    35 #打印
    36 {'name': 1121.1}
    37 传入是数据不是<class 'float'>
    38 {'name': 22}
    支持多类型判断的描述符例子

     (6)、类的装饰器

     1 def Typed(**kwargs):
     2     def decorate(obj):
     3         for k,v in kwargs.items():
     4             setattr(obj,k,v)
     5         return obj
     6     return decorate
     7 
     8 @Typed(x=1,y=2,z=3)
     9 class Foo:
    10     pass
    11 print(Foo.__dict__)
    12 print(Foo.x)
    13 
    14 @Typed(name = "test1")
    15 class Bar:
    16     pass
    17 print(Bar.name)
    18 
    19 #打印
    20 {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'y': 2, 'z': 3}
    21 1
    22 test1
    类的装饰器(带参数)
     1 class Typed:
     2     def __init__(self,name,expect_type):
     3         self.name = name
     4         self.expect_type = expect_type
     5 
     6     def __get__(self, instance, owner):
     7         return instance.__dict__[self.name]
     8 
     9     def __set__(self, instance, value):
    10         if not isinstance(value,self.expect_type):
    11             raise TypeError("类型错误,不是%s" % self.expect_type) # 接收到实例化的值并判断传进来的值的类型是否是装饰器传进来的数据类型。条件满足就赋值。
    12         instance.__dict__[self.name] = value
    13 
    14     def __delete__(self, instance):
    15         instance.__dict__.pop(self.name)
    16 
    17 def func(**kwargs):
    18     def decorate(obj):
    19         for k,v in kwargs.items():
    20             print(k,v)
    21             setattr(obj,k,Typed(k,v)) #重点在这里,这里将k,v传到Typed类中,也就是Typed类的两个属性,name和expect_type。
    22         return obj
    23     return decorate
    24 
    25 @func(name=str,age=int) #(2)在实例化之前有一个func装饰器,装饰器会将Foo类传入func。@dacorate ==》Foo=dacorate(Foo)
    26 class Foo:
    27     def __init__(self,name,age):
    28         self.name = name
    29         self.age = age
    30 
    31 f1 = Foo("test1",19) #(1)实例化的时候将值传给Foo类,
    32 print(f1.__dict__)
    33 
    34 @func(salary=float)
    35 class Bar:
    36     def __init__(self,salary):
    37         self.salary = salary
    38 
    39 b1 = Bar(1000.0)
    40 print(b1.__dict__)
    装饰器+数据描述符来限定数据传入的类型判断

     (7)、自定制property

     1 class Newproperty:
     2     def __init__(self,func):
     3         self.func = func
     4 
     5     def __get__(self, instance, owner):
     6         return self.func(instance)
     7 
     8 class Foo:
     9     def __init__(self,name,width,length):
    10         self.name = name
    11         self.width = width
    12         self.length = length
    13 
    14     @Newproperty   # 等同于 area = Newproperty(area)
    15     def area(self):
    16         return self.width * self.length
    17 
    18 f1 = Foo("房子",10,14)
    19 print(f1.area)
    20 print(f1.__dict__)
    21 
    22 #打印
    23 140
    24 {'name': '房子', 'width': 10, 'length': 14}
    定制property例子
     1 class Lazyproperty:
     2     def __init__(self,func):
     3         self.func = func
     4 
     5     def __get__(self, instance, owner):
     6         value = self.func(instance)
     7         setattr(instance,self.func.__name__,value)  # 这里将值保存到了对象的字典里面。所以当值存在的时候就不会在重复赋值了。
     8         return value
     9     # def __set__(self, instance, value):  # 如果这里加了set,那么优先级就不一样了。属于数据描述符的优先级了。所以这里的延迟计算就失效了。
    10     #     print("aaaa")
    11     
    12 class Foo:
    13     def __init__(self,name,width,length):
    14         self.name = name
    15         self.width = width
    16         self.length = length
    17 
    18     @Lazyproperty   # 等同于 area = Newproperty(area)
    19     def area(self):
    20         return self.width * self.length
    21 
    22 f1 = Foo("房子",10,14)
    23 print(f1.area)
    24 print(f1.__dict__)
    实现延迟计算功能

    (8)、描述符总结

    描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性

    描述符是很多高级哭和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件。

    10、__init__,__del__

    构造函数,析构函数

     析构方法__del__:当对象在内存中被释放时,自动触发执行。

    注:此方法一般无需定义,应为python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都交给python解释器来执行,所以析构函数的调用是有解释器在进行垃圾回收时触发执行的。

    构造函数__init__:用于初始化类的内容部状态,也就是说当该类实例化的时候就会执行该函数。 

    11、__enter__,__exit__

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

     1 class Open:
     2     def __init__(self,name):
     3         self.name = name
     4 
     5     def __enter__(self):
     6         print("出现with语句,对象的__enter__被触发,有返回值这复制给as声明的变量")
     7 
     8     def __exit__(self, exc_type, exc_val, exc_tb):
     9         print("with 中代码执行完毕执行这里。")
    10         print(exc_type)
    11         print(exc_val)
    12         print(exc_tb)
    13         return True   #如果少了这句的话,抛出异常后就不执行下面的代码了
    14 
    15 with Open('a.txt') as f:
    16     print("======>执行代码快")
    17     raise AttributeError("抛出异常")
    18 print("0"*100)
    例子

    __exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

    如果__exit__()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后面的语句正常执行

    好处:

    (1)、使用with语句的目的就是把代码快放入with中执行,with结束后,自动完成清理工作,无须手动干预

    (2)、在需要管理一些资源,比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无需再去关心这个问题。后面的网络编程将大有用处。

    12、property

    13、__doc__

    这个方法是类的描述信息

    特点:这个属性无法被继承 

    14、__module__和__class__

    __module__表示当前操作的对象在哪个模块

    __class__表示当前操作的对象的类是什么

    15、__call__

     对象后面加括号,触发执行。

    注:构造方法的执行是由创建对象触发的,即:对象 = 类名;而对于__call__方法的执行是由对象后加括号触发的,即:对象()或者类()

    16、metaclass,元类

     python中一切皆对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象

    创建类的两种方式:

    (1):

    class Foo:
         def func(self):
             print('from func')

    (2):

    def func(self):
           print('from func')
    x=1
    Foo=type('Foo',(object,),{'func':func,'x':1})

    什么是元类:

    元类是类的类,是类的模版

    元类是用来控制如何创建类的,正如类是创建对象的模版一样。

    元类的实例为类,正如类的实例对象(f1对象是Foo类的一个实例,Foo类是type类的一个实例。)

    type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象。

     1 class Mytype(type):
     2     def __init__(self,what,bases=None,dict=None):
     3         print('mytype init')
     4 
     5     def __call__(self, *args, **kwargs):
     6         obj=self.__new__(self)
     7         self.__init__(obj,*args,**kwargs)
     8         return obj
     9 
    10 class Foo(object,metaclass=Mytype):
    11     x=1111111111
    12 
    13     def __init__(self,name):
    14         self.name=name
    15 
    16     def __new__(cls, *args, **kwargs):
    17         return super().__new__(cls)
    18 
    19 f1=Foo('egon')
    20 
    21 print(f1.__dict__)
    22 
    23 自定制元类纯净版
    自制元类例子
    #元类总结
    class Mymeta(type):
        def __init__(self,name,bases,dic):
            print('===>Mymeta.__init__')
    
    
        def __new__(cls, *args, **kwargs):
            print('===>Mymeta.__new__')
            return type.__new__(cls,*args,**kwargs)
    
        def __call__(self, *args, **kwargs):
            print('aaa')
            obj=self.__new__(self)
            self.__init__(self,*args,**kwargs)
            return obj
    
    class Foo(object,metaclass=Mymeta):
        def __init__(self,name):
            self.name=name
        def __new__(cls, *args, **kwargs):
            return object.__new__(cls)
    
    '''
    需要记住一点:名字加括号的本质(即,任何name()的形式),都是先找到name的爹,然后执行:爹.__call__
    
    而爹.__call__一般做两件事:
    1.调用name.__new__方法并返回一个对象
    2.进而调用name.__init__方法对儿子name进行初始化
    '''
    
    '''
    class 定义Foo,并指定元类为Mymeta,这就相当于要用Mymeta创建一个新的对象Foo,于是相当于执行
    Foo=Mymeta('foo',(...),{...})
    因此我们可以看到,只定义class就会有如下执行效果
    ===>Mymeta.__new__
    ===>Mymeta.__init__
    实际上class Foo(metaclass=Mymeta)是触发了Foo=Mymeta('Foo',(...),{...})操作,
    遇到了名字加括号的形式,即Mymeta(...),于是就去找Mymeta的爹type,然后执行type.__call__(...)方法
    于是触发Mymeta.__new__方法得到一个具体的对象,然后触发Mymeta.__init__方法对对象进行初始化
    '''
    
    '''
    obj=Foo('egon')
    的原理同上
    '''
    
    '''
    总结:元类的难点在于执行顺序很绕,其实我们只需要记住两点就可以了
    1.谁后面跟括号,就从谁的爹中找__call__方法执行
    type->Mymeta->Foo->obj
    Mymeta()触发type.__call__
    Foo()触发Mymeta.__call__
    obj()触发Foo.__call__
    2.__call__内按先后顺序依次调用儿子的__new__和__init__方法
    '''
  • 相关阅读:
    权限设计=功能权限+数据权限
    C# 自定义配置文件
    asp.net core 系列 2 启动类 Startup.CS
    asp.net core 系列 1 概述
    IOC : Unity 配置和使用
    Unity Ioc 依赖倒置及Untity AOP被动拦截/自动拦截
    webuploader-异步切片上传(暂不支持断点续传)及 下载方法!C#/.NET
    C# 构造基础返回值类型-BaseResponse
    神奇的选择器 :focus-within
    妙用 scale 与 transfrom-origin,精准控制动画方向
  • 原文地址:https://www.cnblogs.com/xiaoqianghuihui/p/6780709.html
Copyright © 2020-2023  润新知