我按照檔案中的教程和fluent Python的示例進行操作。在書中,他們教我如何避免AttributeErrorby get,(例如,當你這樣做時z = Testing.x),我想做一些與 set 方法類似的事情。但看起來,它導致了一個沒有錯誤的壞類。
要更具體地說明這個問題:
- 使用注釋掉
Testing.x = 1的行它呼叫__set__方法。 - 對于未注釋的行
#Testing.x = 1,它不會呼叫__set__方法。
有人可以教我為什么它會這樣嗎?
import abc
class Descriptor:
def __init__(self):
cls = self.__class__
self.storage_name = cls.__name__
def __get__(self, instance, owner):
if instance is None:
return self
else:
return getattr(instance, self.storage_name)
def __set__(self, instance, value):
print(instance,self.storage_name)
setattr(instance, self.storage_name, value)
class Validator(Descriptor):
def __set__(self, instance, value):
value = self.validate(instance, value)
super().__set__(instance, value)
@abc.abstractmethod
def validate(self, instance, value):
"""return validated value or raise ValueError"""
class NonNegative(Validator):
def validate(self, instance, value):
if value <= 0:
raise ValueError(f'{value!r} must be > 0')
return value
class Testing:
x = NonNegative()
def __init__(self,number):
self.x = number
#Testing.x = 1
t = Testing(1)
t.x = 1
uj5u.com熱心網友回復:
屬性訪問通常由object.__getattribute__and處理type.__getattribute__(對于 的實體type,即類)。當表單的屬性查找a.x涉及描述符 asx時,各種系結規則將生效,基于以下內容x:
- 實體系結:如果系結到一個物件實體,
a.x則轉換為呼叫:type(a).__dict__['x'].__get__(a, type(a)). - 類系結:如果系結到一個類,
A.x則轉換為呼叫:A.__dict__['x'].__get__(None, A)。 - 超級系結: [...]
對于這個問題的范圍,只有 (2) 是相關的。在這里,Testing.x通過呼叫描述符__get__(None, Testing)。現在有人可能會問,為什么這樣做而不是簡單地回傳描述符物件本身(就好像它是任何其他物件一樣,比如 an int)。此行為對于實作classmethod裝飾器很有用。描述符 HowTo 指南提供了一個示例實作:
class ClassMethod:
def __init__(self, f):
self.f = f
def __get__(self, obj, cls=None):
print(f'{obj = }, {cls = }')
return self.f.__get__(cls, cls) # simplified version
class Test:
@ClassMethod
def func(cls, x):
pass
Test().func(2) # call from instance
Test.func(1) # this requires binding without any instance
我們可以觀察到,對于第二種情況Test.func(1),沒有涉及實體,但ClassMethod描述符仍然可以系結到cls.
鑒于它__get__同時用于實體和類系結,有人可能會問為什么__set__. 具體來說,對于x.y = z,如果y是資料描述符,為什么不呼叫y.__set__(None, z)?我想原因是沒有好的用例,它不必要地使描述符 API 復雜化。無論如何,描述符會如何處理這些資訊?通常,管理屬性的設定方式是由類(或型別的元類)通過object.__setattr__或來完成的type.__setattr__。
因此,為了防止Testing.x被用戶替換,您可以使用自定義元類:
class ProtectDataDescriptors(type):
def __setattr__(self, name, value):
if hasattr(getattr(self, name, None), '__set__'):
raise AttributeError(f'Cannot override data descriptor {name!r}')
super().__setattr__(name, value)
class Testing(metaclass=ProtectDataDescriptors):
x = NonNegative()
def __init__(self, number):
self.x = number
Testing.x = 1 # now this raises AttributeError
但是,這不是絕對保證,因為用戶仍然可以type.__setattr__直接使用來覆寫該屬性:
type.__setattr__(Testing, 'x', 1) # this will bypass ProtectDataDescriptors.__setattr__
uj5u.com熱心網友回復:
線
Testing.x = 1
Testing用整數替換您設定為類屬性的描述符。
由于描述符不再存在,self.x = ...或者t.x = ...只是一個不涉及描述符的賦值。
順便說一句,您肯定已經注意到x您的描述符不再具有真正的屬性,并且您不能在沒有沖突的情況下使用同一描述符的多個實體?
class Testing:
x = NonNegative()
y = NonNegative()
def __init__(self, number):
self.x = number
t = Testing(2345)
t.x = 1234
t.y = 5678
print(vars(t))
列印出來
{'NonNegative': 5678}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/429029.html
標籤:Python python-3.x 哎呀 python 描述符
