2011-05-16 58 views
4

我用__get__(),__set__()和方法to_db()創建了一個class String();然而,當我做name = String(),我不能做self.name.to_db(),因爲它調用to_db()__get__()返回的值,而不是對象「name」。如何調用Python類描述符對象的方法?

class String(object): 

    def __init__(self, value=None): 
     if value: 
      self.value = str(value) 

    def __get__(self, instance, owner): 
     return self.value 

    def __set__(self, instance, value): 
     self.value = str(value) 

    def to_db(self): 
     return {'type':'string', 'value': self.value} 

class Example(object): 

    name = String() 
    age = Integer() 

    def __init__(self,name,age): 
     self.name = name 
     self.age = age 

    def save(): 
     data = dict(name=self.name.to_db(), age=self.age.to_db()) 
     db.save(data) 

一個處理這種方法是不叫self.name.to_db()直接,而是在instance設置標誌和__get__()創建一個條件,以檢查它,並調用to_db()如果它是True,但這似乎缺憾。有沒有更好的辦法?

另外,我是新來的描述符 - 使用instance和/或instance.__dict__來存儲狀態vs存儲在self中的優點/缺點是什麼?

+1

對於任何需要顯式方法調用的東西,不要使用描述符? – delnan 2011-05-16 21:38:38

+2

對我來說,你試圖以錯誤的方式使用描述符。這裏有一個很好的描述:http://docs.python.org/howto/descriptor.html,特別注意property()的實現。 – 2011-05-16 22:30:43

+1

你爲什麼不使用屬性? – 2011-05-16 23:21:25

回答

-1

裏面方法to_db您可以通過

self.__dict__['value'] # value as key is not ideal, but that's what OP used 

直接訪問該值,或者,如果你只使用新式類,

object.__set__(self, name, value) 

由於您使用的魔法屬性,訪問魔術__dict__是完全合理的。

這也被稱爲文檔中爲__setattr__ [1](對不起,存在__set__沒有直接提到__dict__但它同樣的問題域)

If __setattr__() wants to assign to an instance attribute, it should not 
simply execute self.name = value — this would cause a recursive call to itself. 
Instead, it should insert the value in the dictionary of instance attributes, e.g., 
self.__dict__[name] = value. For new-style classes, rather than accessing the instance 
dictionary, it should call the base class method with the same name, for example, 
object.__setattr__(self, name, value). 

[1] http://docs.python.org/2/reference/datamodel.html#customizing-attribute-access

+0

這是一個討厭的解決方法。我會避免用'__dict__'來擺弄。 – delnan 2011-05-16 21:39:05

+0

這不是一個解決方法,如果你正在使用__ get __ – 2011-05-16 22:04:03

+0

也許我錯過了一些東西,但e = Example(name =「Julie」,age =「21」),那麼e.name.to_db()表示>>> e.name.to_db() 回溯(最近通話最後一個): 文件 「」,1號線,在 AttributeError的: '海峽' 對象有沒有屬性 'to_db' 「 - 大概是因爲__get__首先被調用 – espeed 2011-05-16 22:13:32

0

這裏有一個解決方案,允許您繞過該類中定義的任何描述符:

class BypassableDescriptor(object): 
    pass 

class String(BypassableDescriptor): 
    def __init__(self, value=None): 
     if value: 
      self.value = str(value) 

    def __get__(self, instance, owner): 
     return self.value 

    def __set__(self, instance, value): 
     self.value = str(value) 

    def to_db(self): 
     return {'type': 'string', 'value': self.value} 

class Integer(BypassableDescriptor): 
    def __init__(self, value=None): 
     if value: 
      self.value = str(value) 

    def __get__(self, instance, owner): 
     return self.value 

    def __set__(self, instance, value): 
     self.value = int(value) 

    def to_db(self): 
     return {'type': 'integer', 'value': self.value} 

class BypassDescriptor(object): 
    def __init__(self, descriptor): 
     self.descriptor = descriptor 

    def __getattr__(self, name): 
     return getattr(self.descriptor, name) 

class AllowBypassableDescriptors(type): 
    def __new__(cls, name, bases, members): 
     new_members = {} 
     for name, value in members.iteritems(): 
      if isinstance(value, BypassableDescriptor): 
       new_members['real_' + name] = BypassDescriptor(value) 
     members.update(new_members) 
     return type.__new__(cls, name, bases, members) 

class Example(object): 
    __metaclass__ = AllowBypassableDescriptors 

    name = String() 
    age = Integer() 

    def __init__(self,name,age): 
     self.name = name 
     self.age = age 

    def save(self): 
     data = dict(name = self.real_name.to_db(), age = self.real_age.to_db()) 

注意tha它不是完美的 - 你總是需要撥打real_fieldname.method()而不是fieldname.method(),你必須爲所有需要訪問該字段的類使用元類AllowBypassableDescriptors。再一次,這是一個非常兼容的解決方案,可以避免對由描述符包裝的對象進行猴子修補。這就是說,我不確定描述符是你想要做什麼(寫入數據庫?)的最佳解決方案嗎?

6

這很簡單 - 只需要你的描述符返回一個帶有你想要的額外方法的字符串子類。

def __get__(self, instance, owner): 
    class TaggedString(str): 
     def to_db(self): 
      return {'type':'string', 'value': self} 
    return TaggedString(self.value)` 
+0

根據我的上述評論,它並不完全工作如果你真的想使用描述符 - 利用帶標籤的'instance'參數來獲得每個實例級別的'value'。 – 2014-01-02 14:03:44

0

描述符用於描述「它是什麼」或「它是如何工作的」。

例如,我們可以在__get__()__set__()中加入一些限制。

根據你的問題,我想你想添加自己的方法到type<str>,而不是描述如何設置或獲取實例。

所以你可以用下面的代碼來表達你想要做的事情。

class String(str): 
    def __init__(self, value): 
     self.value = value 

    def to_db(self): 
     return {'type':'string', 'value': self.value} 

ss = String('123') 
print ss #123 
print ss.to_db() #{'type':'string', 'value': '123'} 
相關問題