[英]How to use default property descriptors and successfully assign from __init__()?
請問這個的正確成語是什么?
我想定義一個包含可以(可選)從 dict 初始化的屬性的對象(該 dict 來自 JSON;它可能不完整)。 稍后我可能會通過 setter 修改屬性。 實際上有 13 個以上的屬性,我希望能夠使用默認的 getter 和 setter ,但這似乎不適用於這種情況:
但是我不想為所有prop1... propn
編寫顯式描述符prop1... propn
另外,我想將默認賦值從__init__()
移到訪問器中...描述符。
什么是最優雅的解決方案? (除了將所有 setter 調用從__init__()
移到方法/類方法_make()
?)
[刪除評論 使用默認描述符的 badprop 代碼是由於以前的 SO 用戶的評論,他給你的印象是它給了你一個默認的 setter。 但它沒有 - setter 是未定義的,它必然會拋出AttributeError
。]
class DubiousPropertyExample(object):
def __init__(self,dct=None):
self.prop1 = 'some default'
self.prop2 = 'other default'
#self.badprop = 'This throws AttributeError: can\'t set attribute'
if dct is None: dct = dict() # or use defaultdict
for prop,val in dct.items():
self.__setattr__(prop,val)
# How do I do default property descriptors? this is wrong
#@property
#def badprop(self): pass
# Explicit descriptors for all properties - yukk
@property
def prop1(self): return self._prop1
@prop1.setter
def prop1(self,value): self._prop1 = value
@property
def prop2(self): return self._prop2
@prop2.setter
def prop2(self,value): self._prop2 = value
dub = DubiousPropertyExample({'prop2':'crashandburn'})
print dub.__dict__
# {'_prop2': 'crashandburn', '_prop1': 'some default'}
如果您使用第 5 行self.badprop = ...
注釋運行它,則會失敗:
self.badprop = 'This throws AttributeError: can\'t set attribute'
屬性錯誤:無法設置屬性
[一如既往,我閱讀了關於描述符、隱式描述符、從init調用它們的 SO 帖子]
我認為您對屬性的工作方式有些誤解。 沒有“默認設置器”。 它在設置badprop
拋出一個AttributeError
不是因為它還不知道badprop
是一個屬性而不是一個正常的屬性(如果是這種情況,它只會設置沒有錯誤的屬性,因為現在是正常的屬性行為),但是因為你沒有為badprop
提供一個 setter,只有一個 getter。
看看這個:
>>> class Foo(object):
@property
def foo(self):
return self._foo
def __init__(self):
self._foo = 1
>>> f = Foo()
>>> f.foo = 2
Traceback (most recent call last):
File "<pyshell#12>", line 1, in <module>
f.foo = 2
AttributeError: can't set attribute
在構造實例之后,您甚至無法從__init__
外部設置這樣的屬性。 如果您只使用@property
,那么您擁有的是只讀屬性(實際上是一個看起來像屬性讀取的方法調用)。
如果您在 getter 和 setter 中所做的只是將讀/寫訪問重定向到同名但帶有下划線的屬性,那么到目前為止最簡單的事情就是完全擺脫屬性並只使用普通屬性. Python 不是 Java(即使在 Java 中,我也不相信具有明顯公共 getter/setter 的私有字段的優點)。 外部世界可以直接訪問的屬性是“公共”接口的一個完全合理的部分。 如果您后來發現每當讀取/寫入屬性時都需要運行一些代碼,您可以將其設置為屬性,然后無需更改您的接口(這實際上是描述符最初的用途,而不是讓我們可以開始編寫 Java 風格的 getter /setter 每個屬性)。
如果您實際上是在屬性中做一些事情而不是更改屬性的名稱,並且您確實希望您的屬性是只讀的,那么您最好的選擇可能是將__init__
的初始化視為直接設置基礎數據屬性加下划線。 然后你的類可以在沒有AttributeErrors
情況下直接初始化,然后屬性將在讀取屬性時執行它們的操作。
如果您實際上是在屬性中做一些事情而不是更改屬性的名稱,並且您希望您的屬性可讀可寫,那么您需要實際指定獲取/設置它們時會發生什么。 如果每個屬性都有獨立的自定義行為,那么沒有比為每個屬性顯式提供 getter 和 setter 更有效的方法了。
如果您在每個 getter/setter 中運行完全相同(或非常相似)的代碼(並且它不僅僅是在真實的屬性名稱上添加下划線),這就是您反對將它們全部寫出來的原因(沒錯!) ,那么您可能會通過實現__getattr__
、 __getattribute__
和__setattr__
一些來更好地服務。 這些允許您每次將屬性讀取/寫入重定向到相同的代碼(使用屬性名稱作為參數),而不是每個屬性的兩個函數(獲取/設置)。
似乎最簡單的方法是實現__getattr__
和__setattr__
這樣它們就可以訪問您解析的 JSON dict 中的任何鍵,您應該將其設置為實例成員。 或者,您可以使用解析的 JSON 在self.__dict__
上調用update()
,但這並不是處理事情的最佳方式,因為這意味着您的輸入 dict 可能會踐踏實例的成員。
至於你的 setter 和 getter,你應該只在它們實際上做了一些特殊的事情而不是直接設置或檢索有問題的值時才創建它們。 Python 不是 Java(或 C++ 或其他任何東西),您不應該嘗試模仿這些語言中常見的私有/設置/獲取范例。
我只是將 dict 放在本地范圍內,然后在那里獲取/設置我的屬性。
class test(object):
def __init__(self,**kwargs):
self.kwargs = kwargs
#self.value = 20 asign from init is possible
@property
def value(self):
if self.kwargs.get('value') == None:
self.kwargs.update(value=0)#default
return self.kwargs.get('value')
@value.setter
def value(self,v):
print(v) #do something with v
self.kwargs.update(value=v)
x = test()
print(x.value)
x.value = 10
x.value = 5
輸出
0
10
5
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.