[英]Python - can I wrap attribute assignment without writing a getter/setter
這個問題的目標是確定我是否可以包裝設置對象的屬性, 而不只是編寫一個setter然后包裝setter。
我正在嘗試實現一個Observer模式,我不想編寫超出我需要的代碼(所以當然我會寫一個很長的StackOverflow問題,哈哈 - 我認為長期收益是值得的)。
我開始嘗試用一個函數包裝obj.__setattr__
,但它沒有按照我的預期做到,所以現在我想知道如果我不寫一個,我是否可以包裝賦值或改變一個對象的屬性二傳手。
這是我試過的:
class A(object):
pass
def wrapper(obj, func):
def inner(*args, **kwargs):
print "called it"
return func(*args, **kwargs)
return inner
Stick = A()
Stick.__setattr__ = wrapper(Stick, Stick.__setattr__)
Stick.x = 14 #does not print "called it"
如果我只是寫一個setter它會使一個簡單的鈎子,如:
class A(object):
def __init__(self, x):
self.x = x
def set_x(self, new_x):
self.x = x
但我希望能夠以這樣的方式實現Observer模式:每當obj.x
因任何原因發生更改時,偵聽器都會更新。 如果obj.x
是一個int
,例如我可以直接設置它或使用obj.x += some_int
來設置它,所以我想知道是否有一種方法來包裝obj.x
任何/所有設置例如,編寫obj.set_x()
, obj.add_to_x()
, obj.subtract_from_x()
, obj.times_x()
等。
編輯:感謝您指點我的屬性,但我不知道它是如何幫助我以我迄今為止實施的方式包裝它。
例如,如果我有一個這樣的對象:
class Foo(object):
def __init__(self, ex):
self._x = ex
@property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
...這很好,我看到我可以直接修改函數@x.setter
包裝,但我希望創建一些我可以使用以下(虛構的偽代碼)方式的東西:
A.x.setter = observing_function(A, A.x.setter)
...這樣當Ax
發生變化時,會調用observing_function
並完成它將要做的事情。
如果它完全通知答案 - 我正在制作一個'記分牌'來顯示視頻游戲中的中心角色的得分和生命。 而不是在每個循環期間不斷檢查或反復設置(我現在正在做的,這似乎是過度的),我只是希望它在角色的分數/生命發生變化時實際觸發,而包裝器似乎是最好的方法。
我希望避免這種情況:
def add_to_score(self, pts):
self.score += pts
scoreboard_object.text = self.score
...因為對我來說這很糟糕 ,即使它有效。 此外,似乎很多代碼只是為兩個變量建立一個觀察者模式:P
但是, 這就是為什么我寧願事后包裹的制定者。 我有兩個不同的對象,不一定需要有關於彼此的硬編碼數據; 只是一個帶有self.score
屬性的玩家對象和一個帶有self.text
屬性的記分板對象,雖然在它們之間寫“膠水”當然是必要的,但我希望寫入set_score
和set_text
方法不是至關重要的只是為了實現Observer模式。
如果最終我不能跳過寫一個二傳手(或者二傳手),那么我想我會繼續這樣做; 我只是希望避免它。
雖然現在這是一個非常具體的例子,但我也在一般意義上提出要求,因為能夠僅僅觀察變化的屬性而不是圍繞每個屬性進行編碼以准備觀看某些屬性似乎非常方便其他一些對象的變化。 :/
使用屬性訪問的特殊方法,描述符和所有其他方法僅適用於類。 您無法在對象上覆蓋它們。
使用事件(或pub / sub或我們現在稱之為的任何系統)系統。 讓角色負責發出事件,但不是為了記住具體想要他們的人。
class ScoreChanged(Event):
...
class Character(ListenerMixin):
@property
def score(self):
return self._score
@score.setter
def score(self, new):
old = self._score
self._score = new
self.fire_event(ScoreChanged(self, old, new))
protag = Character()
scoreboard = Scoreboard()
scoreboard.listen_event(protag, ScoreChanged, scoreboard.on_score_change)
protag.score += 10
對象必然會在某處糾纏,但它變成了一個實現細節,而不是代碼的主要部分。 特別是,記分板不必是全局的,即使記分板不存在,角色仍然可以正常工作。
在這個問題上有一些實現和現有庫的例子,但是如果你正在編寫一個游戲,你可能已經在使用一個內置的庫。我知道pyglet會這樣做。
新類實例的特殊功能是針對其類而非實例查找的,因此:
class A(object):
pass
def wrapper(func):
def inner(*args, **kwargs):
print "called it"
return func(*args, **kwargs)
return inner
Stick = A()
A.__setattr__ = wrapper(A.__setattr__)
Stick.x = 14 # prints "called it"
Stick.x *= 2 # also prints "called it"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.