python类的相关知识第二部分
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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\n") 18 f1.write("bbbb\n") 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}
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__方法 ‘‘‘
以上是关于python类的相关知识第二部分的主要内容,如果未能解决你的问题,请参考以下文章
python后端面试第二部分:数据储存与缓存相关--长期维护