2012-10-03 71 views
1

這個天真的類試圖模仿基本python對象的屬性訪問。 dictcls顯式存儲屬性和類。其效果是訪問.x的實例將返回dict[x],或者如果失敗,則返回cls.x。就像普通的物體一樣。在Python中完全重寫屬性訪問的正確方法?

class Instance(object): 
    __slots__ = ["dict", "cls"] 
    def __getattribute__(self, key): 
     try: 
      return self.dict[key] 
     except KeyError: 
      return getattr(self.cls, key) 
    def __setattr__(self, key, value): 
     if key == "__class__": 
      self.cls = value 
     else: 
      self.dict[key] = value 

但它沒有那麼簡單。一個明顯的問題是完全無視描述符。試想一下cls有屬性。做Instance.some_property = 10應訪問cls中定義的屬性,但將愉快地將some_property設置爲dict中的屬性。

然後存在clsInstance實例綁定方法的問題,並且可能更多的我甚至不知道。

似乎有很多細節可以讓上面的類儘可能地接近python對象,而我迄今爲止讀過的描述符的文檔並沒有明確說明如何獲取,簡單地說,一切正常。

我所要求的是一個實現python的屬性訪問完整替代的參考。也就是上面的類,但是正確的。

+0

你很明顯知道,實例屬性的「點訪問」並不像看起來那麼容易。它是如何工作的文檔是源代碼。你只需要重新實現。如果您想改變某些內容,請提出更具體的問題。 – Achim

+0

@Achim您不一定使用CPython使用的相同方法,因爲它取決於對Python對象的C級訪問。也許查看[PyPy Python實現](http://pypy.org/)的源代碼會很有幫助,因爲它們已經在(R)Python中實現了真正的Python屬性訪問。 – agf

+0

你希望用這個做什麼? –

回答

1

那麼,我需要這個答案,所以我不得不做這項研究。以下代碼涵蓋以下內容:

  • 數據描述符在設置和獲取屬性時都具有優先權。
  • 非數據描述符正確稱爲__getattribute__

有可能低於我不得不從內部項目轉換代碼錯別字。我不確定它是否像python對象一樣是100%,所以如果任何人都可以發現很棒的錯誤。

_sentinel = object() 

def find_classattr(cls, key): 
    for base in cls.__mro__: # Using __mro__ for speed. 
    try: return base.__dict__[key] 
    except KeyError: pass 
    return _sentinel 

class Instance(object): 
    __slots__ = ["dict", "cls"] 
    def __init__(self, d, cls): 
    object.__setattr__(self, "dict", d) 
    object.__setattr__(self, "cls", cls) 
    def __getattribute__(self, key): 
    d = object.__getattribute__(self, "dict") 
    cls = object.__getattribute__(self, "cls") 
    if key == "__class__": 
     return cls 
    # Data descriptors in the class, defined by presence of '__set__', 
    # overrides any other kind of attribute access. 
    cls_attr = find_classattr(cls, key) 
    if hasattr(cls_attr, '__set__'): 
     return cls_attr.__get__(self, cls) 
    # Next in order of precedence are instance attributes. 
    try: 
     return d[key] 
    except KeyError: 
     # Finally class attributes, that may or may not be non-data descriptors. 
     if hasattr(cls_attr, "__get__"): 
     return cls_attr.__get__(self, cls) 
     if cls_attr is not _sentinel: 
     return cls_attr 

    raise AttributeError("'{}' object has no attribute '{}'".format(
     getattr(cls, '__name__', "?"), key)) 
    def __setattr__(self, key, value): 
    d = object.__getattribute__(self, "dict") 
    cls = object.__getattribute__(self, "cls") 
    if key == "__class__": 
     object.__setattr__(self, "cls", value) 
     return 

    # Again, data descriptors override instance attributes. 
    cls_attr = find_classattr(cls, key) 
    if hasattr(cls_attr, '__set__'): 
     cls_attr.__set__(self, value) 
    else: 
     d[key] = value 

有趣的是,我意識到我已經在幾年前,前寫的一模一樣,但裏面的描述符協議是那麼神祕,因爲我已經忘記了它。

編輯:修正了在使用getattr找到該類的某個屬性時,會在類級調用它的描述符(即沒有該實例)的bug。將其替換爲直接在__dict__中顯示的方法。

+0

如果你想從課外獲得假實例字典,該怎麼辦?你在這裏沒有'cls'的實例,所以如果你在類中調用的方法需要在'__new__'或'__init__'中完成設置呢? – agf

+0

在'Instance .__ init__'的末尾調用'cls .__ init__'是沒有問題的,任何人(需要這種先進的東西)都無法解決如何添加對內部字典的訪問。我個人使用一個工廠,首先創建一個實例,然後用提供的類調用'__init__'。 – porgarmingduod

相關問題