python定义构造函数、包括颜色价格品牌_python基础之面向对象(描述符、类装饰器及元类)...

描述符

描述符(__get__,__set__,__delete__)   # 这里着重描述了python的底层实现原理

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

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

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

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符

2 def __get__(self,instance,owner):3 print('get方法')4 def __set__(self, instance, value):5 print('set方法')6 def __delete__(self, instance):7 print('delete方法')

描述符的简单定义

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classFoo:def __get__(self,instance,owner):print('===>get方法')def __set__(self, instance, value):print('===>set方法')def __delete__(self, instance):print('===>delete方法')#包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法

f1=Foo()

f1.name='egon'

print(f1.name)delf1.name#疑问:何时,何地,会触发这三个方法的执行

引子

3、描述符应用在什么时候,什么地方

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classD:def __get__(self, instance, owner):print("-->get")def __set__(self, instance, value):print("-->set")def __delete__(self, instance):print("-->delete")classE:

e= D() #描述谁?

ee=E()

ee.y= 10 #此时描述的是e y则不会被描述

ee.e #访问e属性,则会触发__get__

ee.e = 2 #为e进行赋值操作,则会触发__set__

del ee.e #删除e的属性,则会触发__delete__#print(ee.__dict__)

简单例子

4、描述符分为俩种形式。

a.数据描述符(至少实现了__get__()和__set__()两种方法)

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classFoo:def __set__(self, instance, value):print('set')def __get__(self, instance, owner):print('get')

数据描述符

b.非数据描述符(没有实现__set__()方法)

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 classFoo:2 def __get__(self, instance, owner):3 print('get')

非数据描述符

注意事项:

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

二、必须把描述符定义成另外一个类触发的类属性,不能为定义到构造函数

5、严格遵循描述符的优先级别,由高到低

a.类属性—》b.数据数据描述符—》c.实例属性—》d.非数据描述符—》e.找不到的属性触发__getattr__()

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 classFoo:2 def __get__(self,instance,owner):3 print('===>get方法')4 def __set__(self, instance, value):5 print('===>set方法')6 def __delete__(self, instance):7 print('===>delete方法')8

9 classBar:10 x=Foo() #调用foo()属性,会触发get方法

11

12 print(Bar.x) #类属性比描述符有更高的优先级,会触发get方法

13 Bar.x=1 #自己定义了一个类属性,并赋值给x,跟描述符没有关系,所以不会触发描述符的方法

14 #print(Bar.__dict__)

15 print(Bar.x)16

17

18 ===>get方法19 None20 1

类属性>数据描述符

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#有get,set就是数据描述符,数据描述符比实例属性有更高的优化级

classFoo:def __get__(self,instance,owner):print('===>get方法')def __set__(self, instance, value):print('===>set方法')def __delete__(self, instance):print('===>delete方法')classBar:

x= Foo() #调用foo()属性,会触发get方法

b1=Bar() #在自己的属性字典里面找,找不到就去类里面找,会触发__get__方法

b1.x #调用一个属性的时候触发get方法

b1.x=1 #为一个属性赋值的时候触发set方法

del b1.x #采用del删除属性时触发delete方法

===>get方法===>set方法===>delete方法

数据描述符>实例属性

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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

2

3 classFoo:4 def __get__(self,instance,owner):5 print('===>get方法')6 def __set__(self, instance, value):7 print('===>set方法')8 def __delete__(self, instance):9 print('===>delete方法')10

11 classBar:12 x = Foo() #调用foo()属性,会触发get方法

13

14 b1=Bar() #实例化

15 Bar.x=11111111111111111 #不会触发get方法

16 b1.x #会触发get方法

17

18 del Bar.x #已经给删除,所以调用不了!报错:AttributeError: 'Bar' object has no attribute 'x'

19 b1.x

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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

classFoo:def __get__(self,instance,owner):print('===>get方法')classBar:

x=Foo()

b1=Bar()

b1.x=1

print(b1.__dict__) #在自己的属性字典里面,{'x': 1}

{'x': 1}

实例属性>非数据描述符

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 #非数据描述符>找不到

2

3 classFoo:4 def __get__(self,instance,owner):5 print('===>get方法')6

7 classBar:8 x =Foo()9 def __getattr__(self, item):10 print('------------>')11

12 b1=Bar()13 b1.xxxxxxxxxxxxxxxxxxx #调用没有的xxxxxxx,就会触发__getattr__方法

14

15

16 ------------> #解发__getattr__方法

非数据描述符>找不到

6、关于描述符的应用(类型检测的应用)

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classTyped:def __get__(self, instance,owner):print('get方法')print('instance参数【%s】' %instance)print('owner参数【%s】' %owner) #owner是显示对象是属于谁拥有的

def __set__(self, instance, value):print('set方法')print('instance参数【%s】' %instance) #instance是被描述类的对象(实例)

print('value参数【%s】' %value) #value是被描述的值

def __delete__(self, instance):print('delete方法')print('instance参数【%s】'%instance)classPeople:

name=Typed()def __init__(self,name,age,salary):

self.name=name #触发的是代理

self.age=age

self.salary=salary

p1=People('alex',13,13.3)#'alex' #触发set方法

p1.name #触发get方法,没有返回值

p1.name='age' #触发set方法

print(p1.__dict__)#{'salary': 13.3, 'age': 13} # 因为name已经被描述,所以实例的属性字典并不存在name#当然也说明一点实例属性的权限并没有数据描述符的权限大

set方法

instance参数【<__main__.people object at>】

value参数【alex】

get方法

instance参数【<__main__.people object at>】

owner参数【】

set方法

instance参数【<__main__.people object at>】

value参数【age】

{'salary': 13.3, 'age': 13}

描述符所包含的参数是什么

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classFoo:def __init__(self,key,pd_type):

self.key=key

self.pd_type=pd_typedef __get__(self, instance, owner):print("get")return instance.__dict__[self.key] #返回值是 instace对象属性字典self.key所对应的值

def __set__(self, instance, value):print(value) #输出value所对应的值

if not isinstance(value,self.pd_type): #判断被描述的值 是否 属于这个类的

raise TypeError("%s 传入的类型不是%s" %(value,self.pd_type)) #为否 则抛出类型异常

instance.__dict__[self.key] = value #True 对instance对象的属性字典进行赋值操作

def __delete__(self, instance):print("delete")

instance.__dict__.pop(self.key) #如果进行删除操作,也是对instance对象的属性字典进行删除操作

classSea:

name= Foo("name",str) #向描述符传入俩个值

history = Foo("history",int)def __init__(self,name,addr,history):

self.name=name

self.addr=addr

self.history=history

s1= Sea("北冰洋","北半球",10000)print(s1.__dict__)print(s1.name) #对被描述的属性进行访问,触发__get__

北冰洋10000{'addr': '北半球', 'history': 10000, 'name': '北冰洋'}

get

北冰洋

用描述符实现的类型检测

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#描述符和类装饰器的结合使用

classFoo:"""描述符"""

def __init__(self,key,pd_type):

self.key=key

self.pd_type=pd_typedef __get__(self, instance, owner):print("get")return instance.__dict__[self.key]def __set__(self,instance,value):"""instance是对象(实例)"""

print("set")if not isinstance(value,self.pd_type): #如果判断类型为False

raise TypeError("%s 传入的类型不是%s" %(value,self.pd_type)) #抛出异常

instance.__dict__[self.key] = value #操作对象(实例)的属性字典设置值

def __delete__(self, instance):print("delete")

instance.__dict__.pop(self.key)def Typed(**kwargs):deffunc(obj):for key,value in kwargs.items(): #遍历字典的键值对

setattr(obj,key,Foo(key,value)) #为Sea的属性字典设置值。 并执行Foo 给Foo传入俩个值

#print("--->" ,obj)

return obj #func的返回值是Sea

returnfunc

@Typed(name=str,addr=str,history=int) #Typed函数运行结束实际上就是 @func --> sea=func(Sea)#Typed运行,并将参数全部传给kwargs

classSea:def __init__(self,name,addr,history):

self.name=name

self.addr=addr

self.history=history

s1= Sea("大西洋","地球",10000)print(s1.__dict__)print(Sea.__dict__) #此时的name,addr,history均被Foo所描述

装饰器和描述符实现类型检测的终极版本

7、描述符总结

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

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

a.利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classRoom:def __init__(self,name,width,length):

self.name=name

self.width=width

self.length=length

@propertydefarea(self):return self.width *self.length

r1=Room(Tom',1,1)

print(r1.area)

@property的特性回顾

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#伪造的property

classWzproperty:def __init__(self,func):

self.func=funcdef __get__(self, instance, owner):"""如果类去调用 instance 为None"""

print("get")if instance isNone:returnself

setattr(instance,self.func.__name__,self.func(instance)) #给实例字典设置值,避免重复计算

returnself.func(instance)classSea:def __init__(self,name,history,speed):

self.name=name

self.history=history

self.speed=speed

@Wzproperty#test = Wzptoperty(test)

deftest(self):return self.history *self.speed

s1= Sea("大西洋",10,20)#print(Sea.__dict__)#print(Sea.test) # 如果类去调用 描述符的instance 此时是None

print(s1.test)print(s1.test) #这一次就不会触发描述符,因为实例属性字典就有

"""因为有了为实例的属性字典设置了结果。所以会率先从自己的属性字典找

其次触发非数据描述符,同时也声明了实例属性的权限大于非数据描述。

如果给描述符+__set__,描述符就变为数据描述符,根据权限实例再去用不会先去

自己的属性字典,而是触发描述符的操作"""

print(s1.__dict__)

控制台输出

get200

200{'test': 200, 'speed': 20, 'name': '大西洋', 'history': 10} #实例的属性字典

伪造的property以及阐释

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#伪造的classmethod

classWzclassmethod:def __init__(self,func):

self.func=funcdef __get__(self, instance, owner):print("get")defbar():return self.func(owner) #test(Sea)

returnbardef __set__(self, instance, value):print("set")classSea:

long= 10kuan= 20@Wzclassmethod#test = Wzclassmethod(test)

deftest(cls):print("长%s 宽%s" %(cls.long,cls.kuan))

Sea.test()

伪造的classmethod

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#伪造的staticmethod

importhashlib,timeclassWzstaticmethod:def __init__(self,func):

self.func=funcdef __set__(self, instance, value):print("set")def __get__(self, instance, owner):print("get")defbar():if instance isNone:returnself.func()returnbardef __delete__(self, instance):print("delete")classOnepiece:def __init__(self):pass@Wzstaticmethod#test = Wzstaticmethod(test)

def test(x=1):

hash=hashlib.md5()

hash.update(str(time.time()).encode("utf-8"))

filename=hash.hexdigest()print(filename)returnfilename#print(Onepiece.__dict__)

Onepiece.test()

伪造的staticmethod

类装饰器

1、基本框架

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

defdeco(func):print('===================')return func #fuc=test

@deco#test=deco(test)

deftest():print('test函数运行')

test()

框架

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

defdeco(obj):print('============',obj)

obj.x=1 #增加属性

obj.y=2obj.z=3

returnobj

@deco#Foo=deco(Foo) #@deco语法糖的基本原理

classFoo:pass

print(Foo.__dict__) #加到类的属性字典中

输出============ {'__module__': '__main__', 'z': 3, 'x': 1, '__dict__': , '__doc__': None, '__weakref__': , 'y': 2}

对类增加类属性

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

def Typed(**kwargs):defdeco(obj):

obj.x=1obj.y=2obj.z=3

returnobjprint('====>',kwargs)returndeco

@Typed(x=2,y=3,z=4) #typed(x=2,y=3,z=4)--->deco 会覆盖原有值

classFoo:pass

View Code

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

def Typed(**kwargs):defdeco(obj):for key,val inkwargs.items():

setattr(obj,key,val)returnobjreturndeco

@Typed(x=1,y=2,z=3) #typed(x=1,y=2,z=3)--->deco

classFoo:pass

print(Foo.__dict__)

@Typed(name='egon')classBar:pass

print(Bar.name)

控制台输出

{'y': 2, '__dict__': , 'z': 3, '__weakref__': , '__module__': '__main__', 'x': 1, '__doc__': None}

egon

增强版

元类

元类(metaclass)

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classFoo:passf1=Foo() #f1是通过Foo类实例化的对象

引子

python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例)

上例可以看出f1是由Foo这个类产生的对象,而Foo本身也是对象,那它又是由哪个类产生的呢?

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#type函数可以查看类型,也可以用来查看对象的类,二者是一样的

print(type(f1)) #输出: 表示,obj 对象由Foo类创建

print(type(Foo)) #输出:

类的爸爸

1、辣么,什么是元类?

元类是类的类,是类的模板

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

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

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

创建类有俩种方式

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classFoo:deffunc(self):print('from func')

方式A,用class定义

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

deffunc(self):print('from func')

x=1Foo=type('Foo',(object,),{'func':func,'x':1})

type要接收三个参数1、将要创建的类名2、继承的类3、类的属性字典

方式B,用type生成

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#方式1

classFoo:pass

#方式2

Bar = type("Bar",(object,),{})print(Foo.__dict__)print(Bar.__dict__)

控制台输出

{'__module__': '__main__', '__doc__': None, '__weakref__': , '__dict__': }

{'__module__': '__main__', '__doc__': None, '__weakref__': , '__dict__': }

俩者间的简单对比

2、一个类没有声明自己的元类,默认它的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的创建,工作流程是什么)

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classMytype(type):def __init__(self,a,b,c):print(self)print(a)print(b)print(c)def __call__(self, *args, **kwargs):print("call")class Slamdunk(metaclass=Mytype): #Mytype("Slamdunk",(object,),{}) 实际上就是这么做,但是传了4个参数

#声明Foo类由Mytype创建,声明自己的元类

def __init__(self,name):

self.name=name

s1= Slamdunk("樱木花道")#根据python一切皆对象,Slamdunk() 本质上就是在触发创建 Slamdunk类的 元类的__call__

控制台输出 #元类创建的实例(对象)

Slamdunk #实例名

() #继承的类,在python3中都默认继承object,即都为新式类

{'__qualname__': 'Slamdunk', '__init__': , '__module__': '__main__'} #实例类的属性字典

call #实例+() 触发了元类的__call__方法

模拟初步认识

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classMytype(type):def __init__(self,a,b,c):print(self)def __call__(self, *args, **kwargs): #传的值是怎么传进去的,就去怎么接收

print("call")

obj= object.__new__(self) #生成一个实例

self.__init__(obj,*args,**kwargs) #这里的self是Mytype产生的实例,这一步触发 Slamdunk 的构造方法

return obj #__call__方法下的返回值是 self 产生的实例 赋值给s1

class Slamdunk(metaclass=Mytype):#Slamdunk = Mytype("Slamdunk",(object,),{}) 实际上就是这么做,但是传了4个参数

#声明Foo类由Mytype创建,声明自己的元类

#触发元类的__init__(元类的构造方法)

def __init__(self,name):

self.name=name

s1= Slamdunk("樱木花道")#根据python一切皆对象,Slamdunk() 本质上就是在触发创建 Slamdunk类的 元类的__call__

print(s1.__dict__) #可以访问到实例对象的属性字典

详解

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classMytype(type):def __init__(self,a,b,c):print(self)def __call__(self, *args, **kwargs):

obj= object.__new__(self)

self.__init__(obj,*args,**kwargs)returnobjclass Slamdunk(metaclass=Mytype):def __init__(self,name):

self.name=name

s1= Slamdunk("樱木花道")print(s1.__dict__)

控制台输出{'name': '樱木花道'}#可以加断点体验

实现创建类的流程 精简版

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值