目錄python
描述符是什麼:描述符本質就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個,這也被稱爲描述符協議緩存
__get__():調用一個屬性時,觸發框架
__set__():爲一個屬性賦值時,觸發函數
__delete__():採用del刪除屬性時,觸發工具
定義一個描述符3d
class Foo: # 在python3中Foo是新式類,它實現了__get__(),__set__(),__delete__()中的一個三種方法的一個,這個類就被稱做一個描述符 def __get__(self, instance, owner): pass def __set__(self, instance, value): pass def __delete__(self, instance): pass
class Foo: def __get__(self, instance, owner): print('觸發get') def __set__(self, instance, value): print('觸發set') def __delete__(self, instance): print('觸發delete') f1 = Foo()
f1.name = 'nick' f1.name del f1.name
class Str: """描述符Str""" def __get__(self, instance, owner): print('Str調用') def __set__(self, instance, value): print('Str設置...') def __delete__(self, instance): print('Str刪除...') class Int: """描述符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 = name self.age = age # 何地?:定義成另一個類的類屬性 # 什麼時候?:且看下列演示 p1 = People('alex', 18)
Str設置... Int設置...
p1.name p1.name = 'nick' del p1.name
Str調用 Str設置... Str刪除...
p1.age p1.age = 18 del p1.age
Int調用 Int設置... Int刪除...
print(p1.__dict__) print(People.__dict__)
{} {'__module__': '__main__', 'name': <__main__.Str object at 0x107a86940>, 'age': <__main__.Int object at 0x107a863c8>, '__init__': <function People.__init__ at 0x107ba2ae8>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
print(type(p1) == People) # type(obj)實際上是查看obj是由哪一個類實例化來的 print(type(p1).__dict__ == People.__dict__)
True True
class Foo: def __set__(self, instance, value): print('set') def __get__(self, instance, owner): print('get')
class Foo: def __get__(self, instance, owner): print('get')
描述符自己應該定義成新式類,被代理的類也應該是新式類代理
必須把描述符定義成這個類的類屬性,不能爲定義到構造函數中code
要嚴格遵循該優先級,優先級由高到底分別是對象
1.類屬性blog
2.數據描述符
3.實例屬性
4.非數據描述符
5.找不到的屬性觸發__getattr__()
class Str: def __init__(self, name): self.name = name def __get__(self, instance, owner): print('get--->', instance, owner) return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->', instance, value) instance.__dict__[self.name] = value def __delete__(self, instance): print('delete--->', instance) instance.__dict__.pop(self.name) class People: name = Str('name') def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary p1 = People('nick', 18, 3231.3)
set---> <__main__.People object at 0x107a86198> nick
print(p1.__dict__)
{'name': 'nick', 'age': 18, 'salary': 3231.3}
print(p1.name)
get---> <__main__.People object at 0x107a86198> <class '__main__.People'> nick
print(p1.__dict__)
{'name': 'nick', 'age': 18, 'salary': 3231.3}
p1.name = 'nicklin' print(p1.__dict__)
set---> <__main__.People object at 0x107a86198> nicklin {'name': 'nicklin', 'age': 18, 'salary': 3231.3}
print(p1.__dict__)
{'name': 'nicklin', 'age': 18, 'salary': 3231.3}
del p1.name print(p1.__dict__)
delete---> <__main__.People object at 0x107a86198> {'age': 18, 'salary': 3231.3}
class Str: def __init__(self, name): self.name = name def __get__(self, instance, owner): print('get--->', instance, owner) return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->', instance, value) instance.__dict__[self.name] = value def __delete__(self, instance): print('delete--->', instance) instance.__dict__.pop(self.name) class People: name = Str('name') def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary # 疑問:若是我用類名去操做屬性呢 try: People.name # 報錯,錯誤的根源在於類去操做屬性時,會把None傳給instance except Exception as e: print(e)
get---> None <class '__main__.People'> 'NoneType' object has no attribute '__dict__'
class Str: def __init__(self, name): self.name = name def __get__(self, instance, owner): print('get--->', instance, owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->', instance, value) instance.__dict__[self.name] = value def __delete__(self, instance): print('delete--->', instance) instance.__dict__.pop(self.name) class People: name = Str('name') def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary print(People.name) # 完美,解決
get---> None <class '__main__.People'> <__main__.Str object at 0x107a86da0>
class Str: def __init__(self, name, expected_type): self.name = name self.expected_type = expected_type def __get__(self, instance, owner): print('get--->', instance, owner) if instance is None: return self return 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] = value def __delete__(self, instance): print('delete--->', instance) instance.__dict__.pop(self.name) class People: name = Str('name', str) # 新增類型限制str def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary try: p1 = People(123, 18, 3333.3) # 傳入的name因不是字符串類型而拋出異常 except Exception as e: print(e)
set---> <__main__.People object at 0x1084cd940> 123 Expected <class 'str'>
class Typed: def __init__(self, name, expected_type): self.name = name self.expected_type = expected_type def __get__(self, instance, owner): print('get--->', instance, owner) if instance is None: return self return 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] = value def __delete__(self, instance): print('delete--->', instance) instance.__dict__.pop(self.name) class People: name = Typed('name', str) age = Typed('name', int) salary = Typed('name', float) def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary try: p1 = People(123, 18, 3333.3) except Exception as e: print(e)
set---> <__main__.People object at 0x1082c7908> 123 Expected <class 'str'>
try: p1 = People('nick', '18', 3333.3) except Exception as e: print(e)
set---> <__main__.People object at 0x1078dd438> nick set---> <__main__.People object at 0x1078dd438> 18 Expected <class 'int'>
p1 = People('nick', 18, 3333.3)
set---> <__main__.People object at 0x1081b3da0> nick set---> <__main__.People object at 0x1081b3da0> 18 set---> <__main__.People object at 0x1081b3da0> 3333.3
def decorate(cls): print('類的裝飾器開始運行啦------>') return cls @decorate # 無參:People = decorate(People) class People: def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary p1 = People('nick', 18, 3333.3)
類的裝飾器開始運行啦------>
def typeassert(**kwargs): def decorate(cls): print('類的裝飾器開始運行啦------>', kwargs) return cls return 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 = name self.age = age self.salary = salary p1 = People('nick', 18, 3333.3)
類的裝飾器開始運行啦------> {'name': <class 'str'>, 'age': <class 'int'>, 'salary': <class 'float'>}
class Typed: def __init__(self, name, expected_type): self.name = name self.expected_type = expected_type def __get__(self, instance, owner): print('get--->', instance, owner) if instance is None: return self return 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] = value def __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 cls return 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 = name self.age = age self.salary = salary print(People.__dict__) p1 = People('nick', 18, 3333.3)
類的裝飾器開始運行啦------> {'name': <class 'str'>, 'age': <class 'int'>, 'salary': <class 'float'>} {'__module__': '__main__', '__init__': <function People.__init__ at 0x10797a400>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None, 'name': <__main__.Typed object at 0x1080b2a58>, 'age': <__main__.Typed object at 0x1080b2ef0>, 'salary': <__main__.Typed object at 0x1080b2c18>} set---> <__main__.People object at 0x1080b22e8> nick set---> <__main__.People object at 0x1080b22e8> 18 set---> <__main__.People object at 0x1080b22e8> 3333.3
描述符是能夠實現大部分python類特性中的底層魔法,包括@classmethod,@staticmethd,@property甚至是__slots__屬性
描述父是不少高級庫和框架的重要工具之一,描述符一般是使用到裝飾器或者元類的大型框架中的一個組件.
class Room: def __init__(self, name, width, length): self.name = name self.width = width self.length = length @property def area(self): return self.width * self.length r1 = Room('alex', 1, 1)
print(r1.area)
1
class Lazyproperty: def __init__(self, func): self.func = func def __get__(self, instance, owner): print('這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area()') if instance is None: return self return self.func(instance) # 此時你應該明白,究竟是誰在爲你作自動傳遞self的事情 class Room: def __init__(self, name, width, length): self.name = name self.width = width self.length = length @Lazyproperty # area=Lazyproperty(area) 至關於定義了一個類屬性,即描述符 def area(self): return self.width * self.length r1 = Room('alex', 1, 1)
print(r1.area)
這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area() 1
class Lazyproperty: def __init__(self, func): self.func = func def __get__(self, instance, owner): print('這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area()') if instance is None: return self else: print('--->') value = self.func(instance) setattr(instance, self.func.__name__, value) # 計算一次就緩存到實例的屬性字典中 return value class Room: def __init__(self, name, width, length): self.name = name self.width = width self.length = length @Lazyproperty # area=Lazyproperty(area) 至關於'定義了一個類屬性,即描述符' def area(self): return self.width * self.length r1 = Room('alex', 1, 1)
print(r1.area) # 先從本身的屬性字典找,沒有再去類的中找,而後出發了area的__get__方法
這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area() ---> 1
print(r1.area) # 先從本身的屬性字典找,找到了,是上次計算的結果,這樣就不用每執行一次都去計算
1
class Lazyproperty: def __init__(self, func): self.func = func def __get__(self, instance, owner): print('這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area()') if instance is None: return self else: value = self.func(instance) instance.__dict__[self.func.__name__] = value return value # return self.func(instance) # 此時你應該明白,究竟是誰在爲你作自動傳遞self的事情 def __set__(self, instance, value): print('hahahahahah') class Room: def __init__(self, name, width, length): self.name = name self.width = width self.length = length @Lazyproperty # area=Lazyproperty(area) 至關於定義了一個類屬性,即描述符 def area(self): return self.width * self.length
print(Room.__dict__)
{'__module__': '__main__', '__init__': <function Room.__init__ at 0x107d53620>, 'area': <__main__.Lazyproperty object at 0x107ba3860>, '__dict__': <attribute '__dict__' of 'Room' objects>, '__weakref__': <attribute '__weakref__' of 'Room' objects>, '__doc__': None}
r1 = Room('alex', 1, 1) print(r1.area) print(r1.area) print(r1.area)
這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area() 1 這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area() 1 這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area() 1
print( r1.area ) #緩存功能失效,每次都去找描述符了,爲什麼,由於描述符實現了set方法,它由非數據描述符變成了數據描述符,數據描述符比實例屬性有更高的優先級,於是全部的屬性操做都去找描述符了
這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area() 1
class ClassMethod: def __init__(self, func): self.func = func def __get__( self, instance, owner): #類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己, def feedback(): print('在這裏能夠加功能啊...') return self.func(owner) return feedback class People: name = 'nick' @ClassMethod # say_hi=ClassMethod(say_hi) def say_hi(cls): print('你好啊,帥哥 %s' % cls.name) People.say_hi() p1 = People()
在這裏能夠加功能啊... 你好啊,帥哥 nick
p1.say_hi()
在這裏能夠加功能啊... 你好啊,帥哥 nick
class ClassMethod: def __init__(self, func): self.func = func def __get__(self, instance, owner ): # 類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己, def feedback(*args, **kwargs): print('在這裏能夠加功能啊...') return self.func(owner, *args, **kwargs) return feedback class People: name = 'nick' @ClassMethod # say_hi=ClassMethod(say_hi) def say_hi(cls, msg): print('你好啊,帥哥 %s %s' % (cls.name, msg)) People.say_hi('你是那偷心的賊') p1 = People()
在這裏能夠加功能啊... 你好啊,帥哥 nick 你是那偷心的賊
p1.say_hi('你是那偷心的賊')
在這裏能夠加功能啊... 你好啊,帥哥 nick 你是那偷心的賊
class StaticMethod: def __init__(self, func): self.func = func def __get__( self, instance, owner): # 類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己 def feedback(*args, **kwargs): print('在這裏能夠加功能啊...') return self.func(*args, **kwargs) return feedback class People: @StaticMethod # say_hi = StaticMethod(say_hi) def say_hi(x, y, z): print('------>', x, y, z) People.say_hi(1, 2, 3) p1 = People()
在這裏能夠加功能啊... ------> 1 2 3
p1.say_hi(4, 5, 6)
在這裏能夠加功能啊... ------> 4 5 6