![](/img/trans.png)
[英]What is the most pythonic way to structure a multi-module python program?
[英]What would be the most pythonic way to make an attribute that can be used in a lambda?
更具體地說,我希望能夠支持lambda: <some_or_other_setter>
,但我想保持代碼簡潔明了。 我必須驗證這個值,所以我需要某種 setter。 我需要使用 lambda 因為我需要將回調傳遞給 Tkinter 事件。 我還需要能夠修改綁定之外的屬性值。
在下面的示例中,假設已全局聲明了一個名為spam_button
的按鈕小部件。 還假設 class Eggs
至少有 10 個屬性,而不是需要以相同的方式訪問(我喜歡一致性)。
我可以做到這一點的第一種可能的方法是只使用 getter 和 setter:
class Eggs(object):
def __init__(self):
self._spam = ''
self.set_spam('Monster')
print self.get_spam()
spam_button.bind('<Enter>', lambda: self.set_spam('Ouch'))
def set_spam(self, spam):
if len(spam) <= 32:
self._spam = spam
def get_spam(self):
return self._spam
但是如果我使用這種方法並且有很多屬性,代碼可能會變得太長而無法管理。
我可以做到這一點的第二種方法是使用屬性,並在回調中使用 setter:
class Eggs(object):
def __init__(self):
self._spam = ''
self.spam = 'Monster'
print self.spam
spam_button.bind('<Enter>', lambda: self.set_spam('Ouch'))
def set_spam(self, spam):
if len(spam) <= 32:
self._spam = spam
def get_spam(self):
return self._spam
spam = property(get_spam, set_spam)
這種方式在直接訪問屬性時比第一種方式簡單一點,但是在使用lambda時就不一致了。
第三種方法是使用 get 和 set 方法制作一個額外的 class :
class Spam(object):
def __init__(self):
self._value = ''
def set(self, value):
if len(spam) <= 32:
self._value = value
def get(self):
return self._value
class Eggs(object):
def __init__(self):
self._spam = ''
self.spam = Spam()
self.spam.set('Monster')
print self.spam.get()
spam_button.bind('<Enter>', lambda: self.spam.set('Ouch'))
這種方法比第一種方法更有條理,但我需要為每種類型的驗證制作一個 class。
我可能做的最后一種方法是使用方法而不是屬性(屬性是第二個例子):
class Eggs(object):
def __init__(self):
self._spam = ''
self.spam('Monster')
print self.spam()
spam_button.bind('<Enter>', lambda: self.spam('Ouch'))
def spam(self, spam=None):
if spam != None:
if len(spam) <= 32:
self._spam = spam
else:
return self._spam
這種方法可能是最短的,但很難一目了然地判斷我是在獲取還是設置。
這些方法中的哪一種(如果有的話)是首選?
Use a closure to return a function to be used as a callback, and define each condition as a function (using lambda
or def
, your preference) instead of each setter as a function.
class Eggs(object):
def __init__(self):
self.spam = 'Monster'
def spam_condition(string = None):
return (string is not None) and (len(string) <= 32)
self.spam_setter = self.set('spam', 'Ouch', spam_condition)
spam_button.bind('<Enter>', self.spam_setter)
self.spam_setter(val='Horse')
def set(self, name, value, condition):
def setter(val):
if type(val) is not .... : # fill this in with the TYPE of the event
value = val
if condition(value):
setattr(self, name, value)
return setter
編輯:現在您可以從任何地方調用設置器,它將檢查條件。
編輯 2:這樣,setter 將檢查它是否收到事件,如果沒有,則使用它傳遞的值。 您還可以使用:
if not isinstance(val, ....) : # fill this in with the CLASS of the event
我仍然建議使用字典並稱之為一天。 將其子類化並覆蓋__setitem__
以進行數據驗證,然后不必擔心 getter:
#!/usr/bin/env python
class Egg(dict):
def __init__(self, *args, **kwargs):
super(Egg, self).__init__(*args, **kwargs)
self['spam'] = 'foo'
spam_button.bind('<Enter>', lambda: self.__setitem__('spam', 'Ouch'))
def __setitem__(self, key, value):
if key == 'spam':
if len(value) > 32:
raise ValueError('"%s" is longer than 32 characters')
return super(Egg, self).__setitem__(key, value)
raise KeyError(key)
可以使用property
處理驗證問題:
class Egg(object):
@property
def spam(self):
return self._spam
@spam.setter
def spam(self, value):
if len(value) <= 32:
self._spam = value
顯然,您仍然可以不受懲罰地使用self._spam = 'spam '*10+'baked beans and spam'
。
使用內置的setattr
:
lambda: setattr(self, 'spam', 'Ouch')
如果您 object 到..."spam"...
並且更喜歡...spam...
,您可以使用property
方法:
lambda: self.__class__.spam.fset(self, 'Ouch')
或者,因為property
是一個描述符:
lambda: type(self).spam.__set__(self, 'Ouch')
但首選第一個版本,我希望有明顯的原因。
我喜歡基於類的方法。 您可能需要進行一組有限的驗證(例如字符串的最大長度、數字的有效范圍等),因此您將擁有有限數量的類。
例如:
class LimitedLengthString(object):
def __init__(self, max_length):
self.max_length = max_length
def set(self, value):
if len(value) <= self.max_length:
self.value = value
def get(self):
return value
class Eggs(object):
def __init__(self):
self.spam = LimitedLengthString(32)
self.spam.set('Monster')
print self.spam.get()
spam_button.bind('<Enter>', lambda: self.spam.set('Ouch'))
如果您確實需要對不同的屬性進行獨特的驗證,那么您將無法避免編寫大量代碼。 但與編程一樣,當您開始重復自己時,請進行概括。
TokenMacGuy 建議后更新:使用相當未知的 Python 功能“描述符”的替代方案:
class LimitedLengthString(object):
def __init__(self, name, max_length):
self.name = name
self.max_length = max_length
def __set__(self, instance, value):
if len(value) <= self.max_length:
instance.__dict__[self.name] = value
def __get__(self, instance, owner):
return instance.__dict__[self.name]
class Eggs(object):
spam = LimitedLengthString('spam', 32)
def __init__(self):
self.spam = 'Monster'
print self.spam # prints 'Monster'
self.spam = 'x' * 40
print self.spam # still 'Monster'
spam_button.bind('<Enter>', lambda: self.spam = 'Ouch')
我發現了一個很好的描述符介紹。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.