Python面向对象—面向对象高级
文章目录
- 一、isinstance和issubclass
- 二、反射
- 1 什么是反射
- 2 python面向对象中的反射
- 3 反射的好处
- 三 、__ setattr__,__ delattr__,__ getattr__
- 四、__ getattribute__
- 五、描述符(__ get__,__ set__,__ delete__)
- 1 什么是描述符
- 2 描述符的作用
- 3 描述符种类
- 4 注意事项
- 5 描述符的使用
- 6 描述符总结
- 六、再看property
- 七、__ setitem__,__ getitem__,__ delitem__
- 八、__ str__,__ repr__,__ format__
- 九、__next__和__iter__实现迭代器协议
- 十、__ doc__
- 十一、__ module__和__class__
- 十二、__ del__
- 十三、__ call__
- 十四、__ enter__和__ exit__
- 十五、__ len__
- 十六、__ hash__
- 十七、__ eq__
- 十八、__ new__和元类metaclass
- 1 什么是元类
- 2 class关键字创建类的流程分析
- 3 自定义元类控制类的创建
- 4 自定义元类控制类的调用
- 5 属性查找
一、isinstance和issubclass
isinstance(obj,cls)检查是否obj是否是类 cls 的对象
class Foo(object):passobj = Foo()isinstance(obj, Foo)
issubclass(sub, super)检查sub类是否是 super 类的派生类
class Foo(object):passclass Bar(Foo):passissubclass(Bar, Foo)
二、反射
1 什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2 python面向对象中的反射
通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
- hasattr(obj, name):判断object中有没有一个name字符串对应的方法或属性
- getattr(obj, name, default=None):从对象获取命名属性;getattr(x,‘y’)等价于x.y。当一个默认参数被给定时,当属性没有被指定时,它被返回存在;没有它,在这种情况下会引发异常。
- setattr(x, y, v):将给定对象上的命名属性设置为指定值。setattr(x,‘y’,v)等价于“x.y=v”
- delattr(x, y):从给定对象中删除命名属性。delattr(x,‘y’)等价于“del x.y”
四个方法的使用演示:
class BlackMedium:feature='Ugly'def __init__(self,name,addr):self.name=nameself.addr=addrdef sell_house(self):print('%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼' %self.name)def rent_house(self):print('%s 黑中介租房子啦,傻逼才租呢' %self.name)b1=BlackMedium('万成置地','回龙观天露园')#检测是否含有某属性
print(hasattr(b1,'name'))
print(hasattr(b1,'sell_house'))#获取属性
n=getattr(b1,'name')
print(n)
func=getattr(b1,'rent_house')
func()# getattr(b1,'aaaaaaaa') #报错
print(getattr(b1,'aaaaaaaa','不存在啊'))#设置属性
setattr(b1,'sb',True)
setattr(b1,'show_name',lambda self:self.name+'sb')
print(b1.__dict__)
print(b1.show_name(b1))#删除属性
delattr(b1,'addr')
delattr(b1,'show_name')
delattr(b1,'show_name111')#不存在,则报错print(b1.__dict__)'''
AttributeError: show_name111
True
True
万成置地
万成置地 黑中介租房子啦,傻逼才租呢
不存在啊
{'name': '万成置地', 'addr': '回龙观天露园', 'sb': True, 'show_name': <function <lambda> at 0x000001CA326CC430>}
万成置地sb
'''
类也是对象:
class Foo(object):staticField = "old boy"def __init__(self):self.name = 'wupeiqi'def func(self):return 'func'@staticmethoddef bar():return 'bar'print(getattr(Foo, 'staticField'))
print(getattr(Foo, 'func'))
print(getattr(Foo, 'bar'))'''
old boy
<function Foo.func at 0x000001A6B27AC5E0>
<function Foo.bar at 0x000001A6B27ACB80>
'''
反射当前模块成员:
import sysdef s1():print('s1')def s2():print('s2')this_module = sys.modules[__name__]print(hasattr(this_module, 's1'))
print(getattr(this_module, 's2'))'''
True
<function s2 at 0x000001F90DF9C670>
'''
导入其他模块,利用反射查找该模块是否存在某个方法
def test():print('from the test')
"""
程序目录:module_test.pyindex.py当前文件:index.py
"""import module_test as obj#obj.test()print(hasattr(obj,'test'))getattr(obj,'test')()
获取当前模块:
current_module=__import__(__name__)
x=111
print(hasattr(current_module,"x"))
print(getattr(current_module,"x"))
3 反射的好处
好处一:可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
例如还未实现全部功能的代码:
class FtpClient:'ftp客户端,但是还么有实现具体的功能'def __init__(self,addr):print('正在连接服务器[%s]' %addr)self.addr=addr
但不影响其他人的代码编写:
#from module import FtpClient
f1=FtpClient('192.168.1.1')
if hasattr(f1,'get'):func_get=getattr(f1,'get')func_get()
else:print('---->不存在此方法')print('处理其他的逻辑')
好处二:动态导入模块(基于反射当前模块成员)
三 、__ setattr__,__ delattr__,__ getattr__
class Foo:x=1def __init__(self,y):self.y=ydef __getattr__(self, item):print('----> from getattr:你找的属性不存在')def __setattr__(self, key, value):print('----> from setattr')# self.key=value #这就无限递归了,你好好想想# self.__dict__[key]=value #应该使用它def __delattr__(self, item):print('----> from delattr')# del self.item #无限递归了self.__dict__.pop(item)#__setattr__添加/修改属性会触发它的执行
f1=Foo(10)
print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z=3
print(f1.__dict__)#__delattr__删除属性的时候会触发
f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)#__getattr__只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx'''
----> from setattr
{}
----> from setattr
{}
----> from delattr
{}
----> from getattr:你找的属性不存在
'''
四、__ getattribute__
回顾__getattr__:
class Foo:def __init__(self,x):self.x=xdef __getattr__(self, item):print('执行的是我')# return self.__dict__[item]f1=Foo(10)
print(f1.x)
f1.xxxxxx #不存在的属性访问,触发__getattr__'''
10
执行的是我
'''
__ getattribute__的用法:
class Foo:def __init__(self,x):self.x=xdef __getattribute__(self, item):print('不管是否存在,我都会执行')f1=Foo(10)
f1.x
f1.xxxxxx'''
不管是否存在,我都会执行
不管是否存在,我都会执行
'''
当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError
class Foo:def __init__(self,x):self.x=xdef __getattr__(self, item):print('执行的是我')# return self.__dict__[item]def __getattribute__(self, item):print('不管是否存在,我都会执行')raise AttributeError('哈哈')f1=Foo(10)
f1.x
f1.xxxxxx'''
不管是否存在,我都会执行
执行的是我
不管是否存在,我都会执行
执行的是我
'''
五、描述符(__ get__,__ set__,__ delete__)
1 什么是描述符
描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__ set__(),__ delete__()中的一个,这也被称为描述符协议。
- __ get__():调用一个属性时,触发
- __ set__():为一个属性赋值时,触发
- __ delete__():采用del删除属性时,触发
定义一个描述符:
class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符def __get__(self, instance, owner):passdef __set__(self, instance, value):passdef __delete__(self, instance):pass
2 描述符的作用
描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
何时何地会触发描述符的三个方法的执行?
#描述符Str
class Str:def __get__(self, instance, owner):print('Str调用')def __set__(self, instance, value):print('Str设置...')def __delete__(self, instance):print('Str删除...')#描述符Int
class Int:def __get__(self, instance, owner):print('Int调用')def __set__(self, instance, value):print('Int设置...')def __delete__(self, instance):print('Int删除...')class People:name=Str()age=Int()def __init__(self,name,age): #name被Str类代理,age被Int类代理,self.name=nameself.age=age#何地?:定义成另外一个类的类属性#何时?:且看下列演示p1=People('alex',18)#描述符Str的使用
p1.name
p1.name='egon'
del p1.name#描述符Int的使用
p1.age
p1.age=18
del p1.age#我们来瞅瞅到底发生了什么
print(p1.__dict__)
print(People.__dict__)#补充
print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的
print(type(p1).__dict__ == People.__dict__)'''
Str设置...
Int设置...
Str调用
Str设置...
Str删除...
Int调用
Int设置...
Int删除...
{}
{'__module__': '__main__', 'name': <__main__.Str object at 0x000001FF6D153FD0>, 'age': <__main__.Int object at 0x000001FF6D153FA0>, '__init__': <function People.__init__ at 0x000001FF6D151700>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
True
True
'''
3 描述符种类
分为两种:
一 数据描述符:至少实现了__get__()和__set__()
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')
4 注意事项
- 一、描述符本身应该定义成新式类,被代理的类也应该是新式类
- 二、必须把描述符定义成这个类的类属性,不能为定义到构造函数中
- 三、要严格遵循该优先级,优先级由高到底分别是:类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 找不到的属性触发__getattr__()
5 描述符的使用
如果我们的类有很多属性,你仍然采用在定义一堆类属性的方式去实现,low
类的装饰器(无参):
def decorate(cls):print('类的装饰器开始运行啦------>')return cls@decorate #无参:People=decorate(People)
class People:def __init__(self,name,age,salary):self.name=nameself.age=ageself.salary=salaryp1=People('egon',18,3333.3)'''
类的装饰器开始运行啦------>
'''
类的装饰器(有参):
def typeassert(**kwargs):def decorate(cls):print('类的装饰器开始运行啦------>',kwargs)return clsreturn decorate
@typeassert(name=str,age=int,salary=float)
#有参:
# 1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs
# 2.People=decorate(People)
class People:def __init__(self,name,age,salary):self.name=nameself.age=ageself.salary=salaryp1=People('egon',18,3333.3)'''
类的装饰器开始运行啦------> {'name': <class 'str'>, 'age': <class 'int'>, 'salary': <class 'float'>}
'''
描述符的使用:
class Typed:def __init__(self,name,expected_type):self.name=nameself.expected_type=expected_typedef __get__(self, instance, owner):print('get--->',instance,owner)if instance is None:return selfreturn instance.__dict__[self.name]def __set__(self, instance, value):print('set--->',instance,value)if not isinstance(value,self.expected_type):raise TypeError('Expected %s' %str(self.expected_type))instance.__dict__[self.name]=valuedef __delete__(self, instance):print('delete--->',instance)instance.__dict__.pop(self.name)def typeassert(**kwargs):def decorate(cls):print('类的装饰器开始运行啦------>',kwargs)for name,expected_type in kwargs.items():setattr(cls,name,Typed(name,expected_type))return clsreturn decorate
@typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
class People:def __init__(self,name,age,salary):self.name=nameself.age=ageself.salary=salaryprint(People.__dict__)
p1=People('egon',18,3333.3)'''
类的装饰器开始运行啦------> {'name': <class 'str'>, 'age': <class 'int'>, 'salary': <class 'float'>}
{'__module__': '__main__', '__init__': <function People.__init__ at 0x00000243FCFB1790>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None, 'name': <__main__.Typed object at 0x00000243FCFB53D0>, 'age': <__main__.Typed object at 0x00000243FCFB5310>, 'salary': <__main__.Typed object at 0x00000243FCFB52B0>}
set---> <__main__.People object at 0x00000243FCFB5FD0> egon
set---> <__main__.People object at 0x00000243FCFB5FD0> 18
set---> <__main__.People object at 0x00000243FCFB5FD0> 3333.3
'''
6 描述符总结
描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性
描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.
六、再看property
一个静态属性property本质就是实现了get,set,delete三种方法
用法一:
class Foo:@propertydef AAA(self):print('get的时候运行我啊')@AAA.setterdef AAA(self,value):print('set的时候运行我啊')@AAA.deleterdef AAA(self):print('delete的时候运行我啊')#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA'''
get的时候运行我啊
set的时候运行我啊
delete的时候运行我啊
'''
用法二:
class Foo:def get_AAA(self):print('get的时候运行我啊')def set_AAA(self,value):print('set的时候运行我啊')def delete_AAA(self):print('delete的时候运行我啊')AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA'''
get的时候运行我啊
set的时候运行我啊
delete的时候运行我啊
'''
实例:
class Goods:def __init__(self):# 原价self.original_price = 100# 折扣self.discount = 0.8@propertydef price(self):# 实际价格 = 原价 * 折扣new_price = self.original_price * self.discountreturn new_price@price.setterdef price(self, value):self.original_price = value@price.deleterdef price(self):del self.original_priceobj = Goods()
obj.price # 获取商品价格
obj.price = 200 # 修改商品原价
print(obj.price)
del obj.price # 删除商品原价
七、__ setitem__,__ getitem__,__ delitem__
用于索引操作,如字典等,分别实现设置、获取、删除等功能
class Foo:def __init__(self,name):self.name=namedef __getitem__(self, item):print(self.__dict__[item])def __setitem__(self, key, value):self.__dict__[key]=valuedef __delitem__(self, key):print('del obj[key]时,我执行')self.__dict__.pop(key)def __delattr__(self, item):print('del obj.key时,我执行')self.__dict__.pop(item)f1=Foo('sb')
f1['age']=18 # 自动触发执行__setitem__方法
f1['age1']=19
del f1.age1 # 自动触发执行__delattr__方法
del f1['age'] # 自动触发执行__delitem__方法
f1['name']='alex'
print(f1.__dict__)'''
del obj.key时,我执行
del obj[key]时,我执行
{'name': 'alex'}
'''
八、__ str__,__ repr__,__ format__
改变对象的字符串显示__str__,__repr __,如果一个类中定义了__str __方法,那么在打印对象时默认输出该方法的返回值
自定制格式化字符串__format__
#_*_coding:utf-8_*_format_dict={'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名
}
class School:def __init__(self,name,addr,type):self.name=nameself.addr=addrself.type=typedef __repr__(self):return 'School(%s,%s)' %(self.name,self.addr)def __str__(self):return '(%s,%s)' %(self.name,self.addr)def __format__(self, format_spec):# if format_specif not format_spec or format_spec not in format_dict:format_spec='nat'fmt=format_dict[format_spec]return fmt.format(obj=self)s1=School('oldboy1','北京','私立')
print('from repr: ',repr(s1))
print('from str: ',str(s1))
print(s1)'''
str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常
'''
print(format(s1,'nat'))
print(format(s1,'tna'))
print(format(s1,'tan'))
print(format(s1,'asfdasdffd'))'''
from repr: School(oldboy1,北京)
from str: (oldboy1,北京)
(oldboy1,北京)
oldboy1-北京-私立
私立:oldboy1:北京
私立/北京/oldboy1
oldboy1-北京-私立
'''
%s和%r:
class B:def __str__(self):return 'str : class B'def __repr__(self):return 'repr : class B'b=B()
print('%s'%b)
print('%r'%b)'''
str : class B
repr : class B
'''
九、__next__和__iter__实现迭代器协议
如果类中有__iter__方法,则该类是一个可迭代对象
其中 对象.__iter__的返回值是迭代器
示例:
class Foo:def __init__(self,x):self.x=xdef __iter__(self):return selfdef __next__(self):n=self.xself.x+=1return self.xf=Foo(3)
for i in f:print(i)
十、__ doc__
表示类的描述信息,且该属性无法被继承
class Foo:'我是描述信息'passprint(Foo.__doc__)'''
我是描述信息
'''
十一、__ module__和__class__
__ module__ 表示当前操作的对象在那个模块
__ class__ 表示当前操作的对象的类是什么
示例文件lib/aa.py:
class C:def __init__(self):self.name = ‘SB'
index.py:
from lib.aa import Cobj = C()
print obj.__module__ # 输出 lib.aa,即:输出模块
print obj.__class__ # 输出 lib.aa.C,即:输出类
十二、__ del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。因此什么时候执行并不知道
示例:
class Foo:def __del__(self):print('执行我啦')f1=Foo()
del f1
print('------->')#输出结果
执行我啦
------->
十三、__ call__
对象后面加括号,触发执行。
注:
- 构造方法__init__的执行是由创建对象触发的,即:对象 = 类名() ;
- 而对于 __ call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:def __init__(self):passdef __call__(self, *args, **kwargs):print('__call__')obj = Foo() # 执行 __init__
obj() # 执行 __call__'''
__call__
'''
十四、__ enter__和__ exit__
我们知道在操作文件对象的时候可以这么写:
with open('a.txt') as f:'代码块'
上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
class Open:def __init__(self,name):self.name=namedef __enter__(self):print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')# return selfdef __exit__(self, exc_type, exc_val, exc_tb):print('with中代码块执行完毕时执行我啊')with Open('a.txt') as f:print('=====>执行代码块')# print(f,f.name)'''
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
'''
__ exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
class Open:def __init__(self,name):self.name=namedef __enter__(self):print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')def __exit__(self, exc_type, exc_val, exc_tb):print('with中代码块执行完毕时执行我啊')print(exc_type)print(exc_val)print(exc_tb)with Open('a.txt') as f:print('=====>执行代码块')raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->不会执行'''
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
<class 'AttributeError'>
***着火啦,救火啊***
<traceback object at 0x00000261F6ABDB80>
Traceback (most recent call last):File "D:\pystudy\fullstack\作业练习\脚本.py", line 18, in <module>raise AttributeError('***着火啦,救火啊***')
AttributeError: ***着火啦,救火啊***
'''
如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
class Open:def __init__(self,name):self.name=namedef __enter__(self):print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')def __exit__(self, exc_type, exc_val, exc_tb):print('with中代码块执行完毕时执行我啊')print(exc_type)print(exc_val)print(exc_tb)return Truewith Open('a.txt') as f:print('=====>执行代码块')raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->会执行
'''
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
<class 'AttributeError'>
***着火啦,救火啊***
<traceback object at 0x00000240B7E8DC00>
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
'''
__ enter__和__ exit__的用途:
使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
十五、__ len__
class A:def __init__(self):self.a = 1self.b = 2def __len__(self):return len(self.__dict__)
a = A()
print(len(a))'''
2
'''
十六、__ hash__
class A:def __init__(self):self.a = 1self.b = 2def __hash__(self):return hash(str(self.a)+str(self.b))
a = A()
print(hash(a))'''
-9051584000431974590
'''
十七、__ eq__
class A:def __init__(self):self.a = 1self.b = 2def __eq__(self,obj):if self.a == obj.a and self.b == obj.b:return True
a = A()
b = A()
print(a == b)'''
True
'''
一道面试题:
class Person:def __init__(self,name,age,sex):self.name = nameself.age = ageself.sex = sexdef __hash__(self):return hash(self.name+self.sex)def __eq__(self, other):if self.name == other.name and self.sex == other.sex:return Truep_lst = []
for i in range(84):p_lst.append(Person('egon',i,'male'))print(p_lst)
print(set(p_lst))
'''
[<__main__.Person object at 0x0000016F4A143FD0>, <__main__.Person object at 0x0000016F4A143F70>, <__main__.Person object at 0x0000016F4A143E80>, <__main__.Person object at 0x0000016F4A143DC0>, <__main__.Person object at 0x0000016F4A143D60>, <__main__.Person object at 0x0000016F4A143790>, <__main__.Person object at 0x0000016F4A143730>, <__main__.Person object at 0x0000016F4A1436D0>, <__main__.Person object at 0x0000016F4A143670>, <__main__.Person object at 0x0000016F4A143610>, <__main__.Person object at 0x0000016F4A1435B0>, <__main__.Person object at 0x0000016F4A143550>, <__main__.Person object at 0x0000016F4A1434F0>, <__main__.Person object at 0x0000016F4A143490>, <__main__.Person object at 0x0000016F4A143430>, <__main__.Person object at 0x0000016F4A1433D0>, <__main__.Person object at 0x0000016F4A143370>, <__main__.Person object at 0x0000016F4A143310>, <__main__.Person object at 0x0000016F4A1432B0>, <__main__.Person object at 0x0000016F4A143250>, <__main__.Person object at 0x0000016F4A1431F0>, <__main__.Person object at 0x0000016F4A143190>, <__main__.Person object at 0x0000016F4A143130>, <__main__.Person object at 0x0000016F4A1430D0>, <__main__.Person object at 0x0000016F4A143070>, <__main__.Person object at 0x0000016F4A142FD0>, <__main__.Person object at 0x0000016F4A142F70>, <__main__.Person object at 0x0000016F4A142F10>, <__main__.Person object at 0x0000016F4A142EB0>, <__main__.Person object at 0x0000016F4A142E50>, <__main__.Person object at 0x0000016F4A142DF0>, <__main__.Person object at 0x0000016F4A142D90>, <__main__.Person object at 0x0000016F4A142D30>, <__main__.Person object at 0x0000016F4A142CD0>, <__main__.Person object at 0x0000016F4A142C70>, <__main__.Person object at 0x0000016F4A142C10>, <__main__.Person object at 0x0000016F4A142BB0>, <__main__.Person object at 0x0000016F4A142880>, <__main__.Person object at 0x0000016F4A142820>, <__main__.Person object at 0x0000016F4A1427C0>, <__main__.Person object at 0x0000016F4A142760>, <__main__.Person object at 0x0000016F4A142700>, <__main__.Person object at 0x0000016F4A1426A0>, <__main__.Person object at 0x0000016F4A142640>, <__main__.Person object at 0x0000016F4A1425E0>, <__main__.Person object at 0x0000016F4A142580>, <__main__.Person object at 0x0000016F4A142520>, <__main__.Person object at 0x0000016F4A1424C0>, <__main__.Person object at 0x0000016F4A142460>, <__main__.Person object at 0x0000016F4A142400>, <__main__.Person object at 0x0000016F4A1423A0>, <__main__.Person object at 0x0000016F4A142340>, <__main__.Person object at 0x0000016F4A1422E0>, <__main__.Person object at 0x0000016F4A142130>, <__main__.Person object at 0x0000016F4A1420D0>, <__main__.Person object at 0x0000016F4A142070>, <__main__.Person object at 0x0000016F4A127AC0>, <__main__.Person object at 0x0000016F4A127B50>, <__main__.Person object at 0x0000016F4A127BB0>, <__main__.Person object at 0x0000016F4A127C10>, <__main__.Person object at 0x0000016F4A127D00>, <__main__.Person object at 0x0000016F49F2B0A0>, <__main__.Person object at 0x0000016F49F2B070>, <__main__.Person object at 0x0000016F49F2BC10>, <__main__.Person object at 0x0000016F4A0C0A90>, <__main__.Person object at 0x0000016F49F25130>, <__main__.Person object at 0x0000016F49EFBBB0>, <__main__.Person object at 0x0000016F49EFBB50>, <__main__.Person object at 0x0000016F49A8B0D0>, <__main__.Person object at 0x0000016F4A145580>, <__main__.Person object at 0x0000016F4A145520>, <__main__.Person object at 0x0000016F4A1454C0>, <__main__.Person object at 0x0000016F4A145460>, <__main__.Person object at 0x0000016F4A145400>, <__main__.Person object at 0x0000016F4A1453A0>, <__main__.Person object at 0x0000016F4A145340>, <__main__.Person object at 0x0000016F4A1452E0>, <__main__.Person object at 0x0000016F4A145280>, <__main__.Person object at 0x0000016F4A145220>, <__main__.Person object at 0x0000016F4A1451C0>, <__main__.Person object at 0x0000016F4A145160>, <__main__.Person object at 0x0000016F4A145100>, <__main__.Person object at 0x0000016F4A1450A0>, <__main__.Person object at 0x0000016F4A145040>]
{<__main__.Person object at 0x0000016F4A143FD0>}'''
十八、__ new__和元类metaclass
1 什么是元类
python中一切皆为对象。让我们先定义一个类:
class OldboyTeacher(object):school='oldboy'def __init__(self,name,age):self.name=nameself.age=agedef say(self):print('%s says welcome to the oldboy to learn Python' %self.name)
所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化),比如对象t1是调用类OldboyTeacher得到的
t1=OldboyTeacher('egon',18)
print(type(t1)) #查看对象t1的类是<class '__main__.OldboyTeacher'>
如果一切皆为对象,那么类OldboyTeacher本质也是一个对象,既然所有的对象都是调用类得到的,那么OldboyTeacher必然也是调用了一个类得到的,这个类称为元类
于是我们可以推导出===>产生OldboyTeacher的过程一定发生了:OldboyTeacher=元类(…)
print(type(OldboyTeacher)) # 结果为<class 'type'>,证明是调用了type这个元类而产生的OldboyTeacher,即默认的元类为type
2 class关键字创建类的流程分析
上文我们基于python中一切皆为对象的概念分析出:我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为type
class关键字在帮我们创建类时,必然帮我们调用了元类OldboyTeacher=type(…),那调用type时传入的参数是什么呢?必然是类的关键组成部分,一个类有三大组成部分,分别是:
- 类名class_name=‘OldboyTeacher’
- 基类们class_bases=(object,)
- 类的名称空间class_dic,类的名称空间是执行类体代码而得到的
调用type时会依次传入以上三个参数
综上,class关键字帮我们创建一个类应该细分为以下四个过程
补充:exec的用法:
三个参数:
- 参数一:包含一系列python代码的字符串
- 参数二:全局作用域(字典形式),如果不指定,默认为globals()
- 参数三:局部作用域(字典形式),如果不指定,默认为locals()
可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中
g={'x':1,'y':2
}
l={}exec('''
global x,z
x=100
z=200m=300
''',g,l)print(g) #{'x': 100, 'y': 2,'z':200,......}
print(l) #{'m': 300}
3 自定义元类控制类的创建
一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类:
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类passclass OldboyTeacher(object,metaclass=Mymeta): # OldboyTeacher=Mymeta('OldboyTeacher',(object),{...})school='oldboy'def __init__(self,name,age):self.name=nameself.age=agedef say(self):print('%s says welcome to the oldboy to learn Python' %self.name)
自定义元类可以控制类的产生过程,类的产生过程其实就是元类的调用过程,即OldboyTeacher=Mymeta(‘OldboyTeacher’,(object),{…}),调用Mymeta会先产生一个空对象OldoyTeacher,然后连同调用Mymeta括号内的参数一同传给Mymeta下的__init__方法,完成初始化,于是我们可以:
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类def __init__(self,class_name,class_bases,class_dic):# print(self) #<class '__main__.OldboyTeacher'># print(class_bases) #(<class 'object'>,)# print(class_dic) #{'__module__': '__main__', '__qualname__': 'OldboyTeacher', 'school': 'oldboy', '__init__': <function OldboyTeacher.__init__ at 0x102b95ae8>, 'say': <function OldboyTeacher.say at 0x10621c6a8>}super(Mymeta, self).__init__(class_name, class_bases, class_dic) # 重用父类的功能if class_name.islower():raise TypeError('类名%s请修改为驼峰体' %class_name)if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0:raise TypeError('类中必须有文档注释,并且文档注释不能为空')class OldboyTeacher(object,metaclass=Mymeta): # OldboyTeacher=Mymeta('OldboyTeacher',(object),{...})"""类OldboyTeacher的文档注释"""school='oldboy'def __init__(self,name,age):self.name=nameself.age=agedef say(self):print('%s says welcome to the oldboy to learn Python' %self.name)
4 自定义元类控制类的调用
调用一个对象,就是触发对象所在类中的__call__方法的执行,如果把OldboyTeacher也当做一个对象,那么在OldboyTeacher这个对象的类中也必然存在一个__call__方法
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类def __call__(self, *args, **kwargs):print(self) #<class '__main__.OldboyTeacher'>print(args) #('egon', 18)print(kwargs) #{}return 123class OldboyTeacher(object,metaclass=Mymeta):school='oldboy'def __init__(self,name,age):self.name=nameself.age=agedef say(self):print('%s says welcome to the oldboy to learn Python' %self.name)# 调用OldboyTeacher就是在调用OldboyTeacher类中的__call__方法
# 然后将OldboyTeacher传给self,溢出的位置参数传给*,溢出的关键字参数传给**
# 调用OldboyTeacher的返回值就是调用__call__的返回值
t1=OldboyTeacher('egon',18)
print(t1) '''
<class '__main__.OldboyTeacher'>
('egon', 18)
{}
123
'''
默认地,调用t1=OldboyTeacher(‘egon’,18)会做三件事:
- 产生一个空对象obj
- 调用__init__方法初始化对象obj
- 返回初始化好的obj
对应着,OldboyTeacher类中的__call__方法也应该做这三件事
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>#1、调用__new__产生一个空对象objobj=self.__new__(self) # 此处的self是类OldoyTeacher,必须传参,代表创建一个OldboyTeacher的对象obj#2、调用__init__初始化空对象objself.__init__(obj,*args,**kwargs)#3、返回初始化好的对象objreturn objclass OldboyTeacher(object,metaclass=Mymeta):school='oldboy'def __init__(self,name,age):self.name=nameself.age=agedef say(self):print('%s says welcome to the oldboy to learn Python' %self.name)t1=OldboyTeacher('egon',18)
print(t1.__dict__) '''
{'name': 'egon', 'age': 18}
'''
上例的__call__相当于一个模板,我们可以在该基础上改写__call__的逻辑从而控制调用OldboyTeacher的过程,比如将OldboyTeacher的对象的所有属性都变成私有的
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>#1、调用__new__产生一个空对象objobj=self.__new__(self) # 此处的self是类OldoyTeacher,必须传参,代表创建一个OldboyTeacher的对象obj#2、调用__init__初始化空对象objself.__init__(obj,*args,**kwargs)# 在初始化之后,obj.__dict__里就有值了obj.__dict__={'_%s__%s' %(self.__name__,k):v for k,v in obj.__dict__.items()}#3、返回初始化好的对象objreturn objclass OldboyTeacher(object,metaclass=Mymeta):school='oldboy'def __init__(self,name,age):self.name=nameself.age=agedef say(self):print('%s says welcome to the oldboy to learn Python' %self.name)t1=OldboyTeacher('egon',18)
print(t1.__dict__) '''
{'_OldboyTeacher__name': 'egon', '_OldboyTeacher__age': 18}
'''
上例中涉及到查找属性的问题,比如self.new,请看下一小节
5 属性查找
结合python继承的实现原理+元类重新看属性的查找应该是什么样子呢???
在学习完元类后,其实我们用class自定义的类也全都是对象(包括object类本身也是元类type的 一个实例,可以用type(object)查看),我们学习过继承的实现原理,如果把类当成对象去看,将下述继承应该说成是:对象OldboyTeacher继承对象Foo,对象Foo继承对象Bar,对象Bar继承对象object
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类n=444def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>obj=self.__new__(self)self.__init__(obj,*args,**kwargs)return objclass Bar(object):n=333class Foo(Bar):n=222class OldboyTeacher(Foo,metaclass=Mymeta):n=111school='oldboy'def __init__(self,name,age):self.name=nameself.age=agedef say(self):print('%s says welcome to the oldboy to learn Python' %self.name)print(OldboyTeacher.n) #自下而上依次注释各个类中的n=xxx,然后重新运行程序,发现n的查找顺序为OldboyTeacher->Foo->Bar->object->Mymeta->type
于是属性查找应该分成两层,一层是对象层(基于c3算法的MRO)的查找,另外一个层则是类层(即元类层)的查找
查找顺序:
1、先对象层:OldoyTeacher->Foo->Bar->object
2、然后元类层:Mymeta->type
依据上述总结,我们来分析下元类Mymeta中__call__里的self.__new__的查找
class Mymeta(type): n=444def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>obj=self.__new__(self)print(self.__new__ is object.__new__) #Trueclass Bar(object):n=333# def __new__(cls, *args, **kwargs):# print('Bar.__new__')class Foo(Bar):n=222# def __new__(cls, *args, **kwargs):# print('Foo.__new__')class OldboyTeacher(Foo,metaclass=Mymeta):n=111school='oldboy'def __init__(self,name,age):self.name=nameself.age=agedef say(self):print('%s says welcome to the oldboy to learn Python' %self.name)# def __new__(cls, *args, **kwargs):# print('OldboyTeacher.__new__')OldboyTeacher('egon',18) #触发OldboyTeacher的类中的__call__方法的执行,进而执行self.__new__开始查找
总结,Mymeta下的__call__里的self.new__在OldboyTeacher、Foo、Bar里都没有找到__new__的情况下,会去找object里的__new,而object下默认就有一个__new__,所以即便是之前的类均未实现__new__,也一定会在object中找到一个,根本不会、也根本没必要再去找元类Mymeta->type中查找__new__
我们在元类的__call__中也可以用object.new(self)去造对象
但我们还是推荐在__call__中使用self.new(self)去创造空对象,因为这种方式会检索三个类OldboyTeacher->Foo->Bar,而object.__new__则是直接跨过了他们三个
最后说明一点
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类n=444def __new__(cls, *args, **kwargs):obj=type.__new__(cls,*args,**kwargs) # 必须按照这种传值方式print(obj.__dict__)# return obj # 只有在返回值是type的对象时,才会触发下面的__init__return 123def __init__(self,class_name,class_bases,class_dic):print('run。。。')class OldboyTeacher(object,metaclass=Mymeta): #OldboyTeacher=Mymeta('OldboyTeacher',(object),{...})n=111school='oldboy'def __init__(self,name,age):self.name=nameself.age=agedef say(self):print('%s says welcome to the oldboy to learn Python' %self.name)print(type(Mymeta)) #<class 'type'>
# 产生类OldboyTeacher的过程就是在调用Mymeta,而Mymeta也是type类的一个对象,那么Mymeta之所以可以调用,一定是在元类type中有一个__call__方法
# 该方法中同样需要做至少三件事:
# class type:
# def __call__(self, *args, **kwargs): #self=<class '__main__.Mymeta'>
# obj=self.__new__(self,*args,**kwargs) # 产生Mymeta的一个对象
# self.__init__(obj,*args,**kwargs)
# return obj
Python面向对象—面向对象高级相关推荐
- week5 day4 面向对象编程高级
week5 day4 面向对象编程高级 一. 判断是不是对象,是不是子类 二. 反射 2.1 反射机制的实现原理 2.2 四个内置函数的使用 2.3 用反射的好处 三. 内置方法 3.1 `__set ...
- 16.1、python初识面向对象(1)
初识面向对象 楔子 你现在是一家游戏公司的开发人员,现在需要你开发一款叫做<人狗大战>的游戏,你就思考呀,人狗作战,那至少需要2个角色,一个是人, 一个是狗,且人和狗都有不同的技能,比如人 ...
- 《C#精彩实例教程》小组阅读12 -- C#面向对象技术高级应用
本微信图文详细介绍了C#面向对象技术高级应用.
- Python设计模式面向对象编程
前言 本篇文章是基于极客时间王争的<设计模式之美>做的总结和自己的理解. 说到面向对象编程,作为一个合格的Pythoner,可以说信手拈来.毕竟在Python里"万物都是对象 ...
- Python初识面向对象
一.Python初识面向对象 1.1 封装 class Person:country='中国' #静态字段,可以直接调用def __init__(self,name,pwd): #Python类自带的 ...
- Python之面向对象类和对象
Python之面向对象类和对象 定义一个类:class 定义类的语法: class Test(object):"""类里定义一类事物共同的技能.可以是变量,也可是函数.& ...
- Python之面向对象进阶
Python之面向对象进阶 进阶有:Python 类的成员.成员修饰符.类的特殊成员. 一.类的成员 类的成员可以分为三大类:字段.方法和属性. 注:所有成员中,只有普通字段的内容保存对象中,即:根据 ...
- python 内存溢出能捕获吗_从0基础学习Python (19)[面向对象开发过程中的异常(捕获异常~相关)]...
从0基础学习Python (Day19) 面向对象开发过程中的=>异常 什么是异常 当程序在运行过程中出现的一些错误,或者语法逻辑出现问题,解释器此时无法继续正常执行了,反而出现了一些错误的 ...
- Python之面向对象继承和派生
Python之面向对象继承和派生 什么是继承: 继承是一种创建新的类的方法.在Python中,新建的类可以继承自一个或多个父类.原始类称为基类或超类. 新建的类称为派生类或子类. Python中类的继 ...
- 编程学习笔记(第一篇)面向对象技术高级课程:绪论-软件开发方法的演化与最新趋势(1)...
软件工程的课程,对于从事大中型的软件开发是至关重要的一门课程. <面向对象技术高级课程>深入.系统.完整地讲解当今主流的面向对象软件开发方法的分析.设计.实现及重构方法,深入讲解UML语言 ...
最新文章
- 超详细的java生成excel文件并下载
- 土地一分用计算机怎么算,一分地等于多少平方米怎么算
- 有程序在记录你的键盘输入_12个用Java编写基础小程序amp;经典案例(收藏)
- SAP LSMW批导数据的几个注意点
- 在没有任何前端开发经验的基础上, 创建第一个 SAP Fiori Elements 应用
- Service Worker 的一个实战例子
- 爬虫结果数据完整性校验
- 前端学习(2156):uglifyjswebpackplugin的使用
- 二维教组A[12][18]采用列优先的存储方法,若每个元素各占3个存储单元,且第1个元素的地址为150,则元素A[9][7]的地址为 ( )
- 用mysql + node搭建一个简易工作列表网站
- linux指定的文件不是虚拟磁盘,linux下挂载VHD等虚拟磁盘文件的方法
- 打印pdf文件 vfp_新技能,如何将多份pdf电子发票文件合成一份文档打印
- url 的正则表达式:path-to-regexp
- 多个生产者多个消费者,只有5个包子
- 卖两本windows phone的书
- iOS常用---NSString,NSMutabuleString
- java 新浪博客_Java
- Matlab绘制隐式函数形成曲面的方法总结(转载)
- 百度旋转验证码打码模块,集成鱼刺模块类
- 武汉大学计算机学院 工程硕士,武汉大学计算机学院在职人员攻读工程硕士学位招生专业介绍...