描述符(__get__,__set__,__delete__)
目錄
- 描述符
- 描述符的作用
- 何時,何地,會觸發這三個方法的執行
- 兩種描述符
- 資料描述符
- 非資料描述符
- 描述符注意事項
- 使用描述符
- 牛刀小試
- 拔刀相助
- 磨刀霍霍
- 大刀闊斧
- 類的裝飾器:無參
- 類的裝飾器:有參
- 刀光劍影
- 描述符總結
- 自定製@property
- property回顧
- 自定製property
- 實現延遲計算功能
- 打破延遲計算
- 自定製@classmethod
- 自定製@staticmethod
描述符
描述符是什麼:描述符本質就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個,這也被稱為描述符協議
__get__():呼叫一個屬性時,觸發
__set__():為一個屬性賦值時,觸發
__delete__():採用del刪除屬性時,觸發
定義一個描述符
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設定...
- 描述符Str的使用
p1.name
p1.name = 'nick'
del p1.name
Str呼叫
Str設定...
Str刪除...
- 描述符Int的使用
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
兩種描述符
資料描述符
- 至少實現了__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')
描述符注意事項
描述符本身應該定義成新式類,被代理的類也應該是新式類
必須把描述符定義成這個類的類屬性,不能為定義到建構函式中
要嚴格遵循該優先順序,優先順序由高到底分別是
1.類屬性
2.資料描述符
3.例項屬性
4.非資料描述符
5.找不到的屬性觸發__getattr__()
使用描述符
- 眾所周知,python是弱型別語言,即引數的賦值沒有型別限制,下面我們通過描述符機制來實現型別限制功能
牛刀小試
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__'
- 修訂__get__方法
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
- 大刀闊斧之後我們已然能實現功能了,但是問題是,如果我們的類有很多屬性,你仍然採用在定義一堆類屬性的方式去實現,low,這時候我需要教你一招:獨孤九劍
類的裝飾器:無參
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__屬性
描述父是很多高階庫和框架的重要工具之一,描述符通常是使用到裝飾器或者元類的大型框架中的一個元件.
自定製@property
- 利用描述符原理完成一個自定製@property,實現延遲計算(本質就是把一個函式屬性利用裝飾器原理做成一個描述符:類的屬性字典中函式名為key,value為描述符類產生的物件)
property回顧
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
自定製property
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
自定製@classmethod
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 你是那偷心的賊
自定製@staticmethod
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
相關推薦
Python進階-----描述符(__get__(),__set__(),__delete__())
一、描述符是什麼 描述符本質就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個,這也被稱為描述符協議 __get__():呼叫一個屬性時,觸發 __set__():為一個屬性賦值時,觸發 __delete__():採用del刪除屬
python3:屬性描述符(__get__,__set__,__delete__)
我先問大家個問題。 如何對屬性進行校驗呢 比如有個要保證 age 屬性 它只能是int型別且大小處於0到100之間? 第一直覺就是用get 和set 方法如下: 1.基本的呼叫 set 和get 方法 class Student: def __init__(self)
Python描述符(__get__,__set__,__delete__)簡介
for 原因 python 原理 none 對象屬性 描述符 簡介 訪問 先說定義,這裏直接翻譯官方英文文檔: 一般來說,描述符是具有“綁定行為”的對象屬性,該對象的屬性訪問將會被描述符協議中的方法覆蓋.這些方法是__get__(),__set
描述符(__get__,__set__,__delete__)
目錄 描述符 描述符的作用 何時,何地,會觸發這三個方法的執行 兩種描述符 資料描述符 非資料描述符 描述符注意事項
__get__,__set__,__delete__
# class Str: # def __get__(self, instance, owner): # print('str__get__') # def __set__(self, instance, value): # print('str __se
python關於描述符__set__ ,__get__ , __delete__
1.描述符 The following methods only apply when an instance of the class containing the method (a so-called descriptor class) appears in an owne
Python語言學習講解十六:python之描述符__set__和__get__ 等解釋
注:每週一到週五都會進行相關Python基礎知識更新,歡迎大家提寶貴的意見 一、方法: 首先說下python中存在的幾種方法:物件方法、靜態方法、類方法等,歸屬權分別為obj、cls、cls 其實可以從他們的引數中就可以看的出來 物件方法引數中含有self,這個
進程管理—進程描述符(task_struct)
umask 之間 css_ linux平臺 contexts cor 順序 unlock code http://blog.csdn.net/qq_26768741/article/details/54348586 當把一個程序加載到內存當中,此時,這個時候就有了進程,關於
文件描述符fd、文件指針fp和vfork()
運行 color 調用exe urn 系統 通用 依賴 使用 src 1. fd:在形式上是一個非負整數.實際上他是一個索引值、指向kernal為每一個進程所維護的該進程打開文件的記錄表. 當程序打開一個文件或者創建一個新文件的時候kernal向進程返回一個文件
ORA-12514: TNS: 監聽程序當前無法識別連接描述符中請求的服務解決
generate 啟動 port 不同 技術 sid ati 請求 desc 問題:Oracle主服務和監聽器服務已經啟動,使用SQL Plus能夠正常連接,使用PL SQL Developer連接報次錯誤:ORA-12514: TNS: 監聽程序當前無法識別連接描述符中請
類和對象描述符
sel def import 應用場景 property 對象 number ati 描述符 @staticmethod 在類裏面給函數添加靜態方法,用法詳見有理數類,此方法使用於定義在類裏面的函數,它所描述的方法應該是在類裏面定義的一個非實例方法,這個方法只使用於這個
關於套接字描述符
關系 具體細節 socket 什麽 想象 分享 地址 增加 mfa 什麽是套接字描述符?(socket描述符) 套接字描述符是一個整數類型的值。每個進程的進程空間裏都有一個套接字描述符表,該表中存放著套接字描述符和套接字數據結構的對應關系。該表中有一個字段存放新創建的
linux套接字或者文件描述符的未讀取得字節數FIONREAD,MSG_PEEK標誌
返回值 strong linu 非阻塞 讀數 減少 另有 如果 第一次 FIONREAD,就是返回緩沖區有多少字節。輸入有個輸入緩沖區,用int nread;ioctl(0,FIONREAD,&nread);能得到緩沖區裏面有多少字節要被讀取。值放在 nread裏面
Iterator Protocol - Python 描述符協議
var 列表 下一個 可叠代對象 data xib 通過 stop comm 1 Iterator Protocol - Python 描述符協議 2 3 先看幾個有關概念, 4 iterator 叠代器, 5 一個實現了無參
JNI字段描述符
ava 標示 編碼 border size 引用類型 face string short “([Ljava/lang/String;)V” 它是一種對函數返回值和參數的編碼。這種編碼叫做JNI字段描述符(JavaNative Interface FieldDescript
JavaScript中的對象描述符(屬性特性)
http rabl catch defined tor pro 其他 mas art 我們先創建一個對象: var person = { name: "Nicholas", _job: "Software Engineer", sayName: funct
Linux中斷 - IRQ number和中斷描述符
else pla 中斷控制 詳細 打開 fig 好的 應該 啟動 一、前言 本文主要圍繞IRQ number和中斷描述符(interrupt descriptor)這兩個概念描述通用中斷處理過程。第二章主要描述基本概念,包括什麽是IRQ number,什麽是中斷描述符等。第
ORA-12505: TNS: 監聽程序當前無法識別連接描述符中所給出的SID等錯誤解決方法
RR script 監聽 修改 查詢 listen 註冊 其他 esc 程序連接orarle報ORA-12505錯誤 一、異常{ ORA-12505, TNS:listener does not currently know of SID given in connect
Linux-進程描述符 task_struct 詳解
可能 基於 常用 use bus 系統資源 17. float entity 為了描述控制進程的運行,系統中存放進程的管理和控制信息的數據結構稱為進程控制塊 PCB(Process Control Block),它是進程實體的一部分,是操作系統中最重要的記錄性數據結構。
操作系統學習(五) 、代碼段和數據段描述符
數據段 轉移 異常類 格式 需要 狀態 管理 更新 不能 一、代碼段和數據段描述符格式 段描述符通用格式如下所示: 代碼段和數據段描述符中各個位的含義如下所示: 二、代碼段和數據段描述符類型 當段描述符中S標誌位(描述符類型)被置位,則該描述符用於代碼段或數據段。