2011-09-21 41 views
2

見下提取賦值的變量名

更新我甚至不知道如何讓我的問題的簡短標題。

在I類有StringField一些類的類屬性:

class Authors(Table): 
    # id field is already present 
    first_name = StringField(maxLength=100) 
    last_name = StringField(maxLength=100) 

StringField構造可能會收到一個參數調用name。如果沒有給出,我希望它等於班級屬性的名稱(在上例中爲first_name,last_name)。

是否可以提取創建的實例將分配給變量的名稱? 我想我必須使用inspect模塊?

我看到Django的做到這一點:

每個字段類型,除了ForeignKey的,ManyToManyField和 OneToOneField,有一個可選的第一個位置參數 - 一個 冗長的名字。如果沒有給出詳細名稱,Django將使用字段的屬性名稱 自動創建它,並將 下劃線轉換爲空格。

在這個例子中,詳細名稱是 「人的名字」:

first_name = models.CharField("person's first name", max_length=30)

在這個例子中,詳細名稱是 「名」:

first_name = models.CharField(max_length=30)

但我沒有在Django 1.3.1源代碼中找到正在做我需要的部分。

UPDATE:

爲了簡化:

class Field(): 
    def __init__(self, field_name=None): 
     if not field_name: 
      field_name = ??? # some magic here to determine the name 
     print(field_name) 

class Table(): 
    first_name = Field() 
    last_name = Field() 

運行這個應該打印first_namelast_name

SOLUTION

class Field(): 
    def __init__(self, name=None): 
     self._name = name 

class Table(): 
    first_name = Field() 
    last_name = Field() 



for attrName, attr in Table.__dict__.items(): 
    if isinstance(attr, Field): 
     if attr._name is None: 
      attr._name = attrName 

print(Table.first_name._name) 
print(Table.last_name._name) 
+0

StringField是否從django.db.models.Field擴展?你能發佈StringField的來源嗎?或者如果你沒有寫它,告訴我們你從哪裏獲得StringField,因爲它不是Django的一部分。 –

+0

你不是讓這個更復雜嗎?你能否介紹一下你使用StringField的原因? – LaundroMat

+0

我引用了Django文檔,因爲我看到Django使用這個 - 解釋我想要的內容,並且找不到相應的代碼。 StringField是我的自定義類。我只是想實現類似於Django的行爲。 – warvariuc

回答

2

我不知道Django是如何做到的。但是你可以這樣來做:

class WantFixup(object): 
    def new_instance(self, name, derived_name): 
     cls = type(self) 
     if name is None: 
      name = derived_name.replace('_', ' ') 
     return cls(name) 

class Container(WantFixup): 
    def __init__(self, name=None): 
     self.name = name 
    def __repr__(self): 
     return "Container('%s')" % str(self.name) 

class WillFixup(object): 
    def __init__(self): 
     cls = type(self) 
     for name in cls.__dict__: 
      o = cls.__dict__[name] # look up object from name 
      if not isinstance(o, WantFixup): 
       continue 
      print("calling %s.new_instance('%s', '%s')" % (o, o.name, name)) 
      self.__dict__[name] = o.new_instance(o.name, name) 

class Name(WillFixup): 
    first_name = Container("given name") 
    last_name = Container() 

這裏是在行動上面的代碼示例:

>>> import auto_name 
>>> n = auto_name.Name() 
calling Container('None').new_instance('None', 'last_name') 
calling Container('given name').new_instance('given name', 'first_name') 
>>> print(n.__dict__) 
{'first_name': Container('given name'), 'last_name': Container('last name')} 
>>> print(auto_name.Name.__dict__) 
{'__module__': 'auto_name', 'last_name': Container('None'), 'first_name': Container('given name'), '__doc__': None} 
>>> 

WantFixup有兩個目的。首先,所有從它繼承的類都可以使用isinstance()進行檢測;如果我們的對象實例名爲o,那麼我們可以測試它,如isinstance(o, WantFixup)。其次,它提供.new_instance()方法函數給任何繼承自它的類。

Container是,可能需要修正的容器的一個例子。請注意,它從WantFixup繼承。

WillFixup類包含一個.__init__()方法,該方法對所有從它繼承的類執行修復。這只是遍歷類字典中的所有內容,併爲每個函數調用.new_instance()方法函數,並傳入名稱。

最後,從WillFixupName繼承和包含Container兩個實例。因爲它繼承自WillFixup,所以調用方法WillFixup.__init__()。從示例中可以看出,first_name.name屬性設置爲'given name',但未設置last_name,因此將其修補爲.name屬性設置爲'last name'

.__init__()功能應該建立新的類實例。只要所有特殊的WantFixup類實例都在父類中,.__init__()方法就會自動循環並設置它們。

這裏令人困惑的部分是,該實例將first_name設置爲Container的實例,名稱爲patched,實際上將用於存儲內容。但類Name包含Container的實例,該實例僅用於存儲類的名稱,並作爲.__init__()方法的標記來查找。

好部分是神奇的是基類中隱藏起來。 ContainerName類只是需要從它們繼承,但不是自己雜亂無章。

有可能是使用元編程來解決問題的巧妙方式。

http://www.ibm.com/developerworks/linux/library/l-pymeta/index.html

該解決方案是不是元類節目,但它是測試,工作代碼。

編輯:這是代碼的更改版本。原始代碼旨在顯示總體思路,但實際上並沒有啓動對象。實際執行init並不難,所以我改變了它。

+0

請參閱更新。 – warvariuc

+0

代碼更改和改進。也許現在會更有意義。 – steveha

+0

我沒有完全理解你的代碼,但我想我已經明白了。我會將解決方案添加到問題中。 – warvariuc

1

爲了讓魔法發生在示例中,Python需要是一個上下文敏感的語言(據我所知,這不是那麼遠)。 Django使用ModelBase元類來(除其他任務外)爲字段設置詳細名稱。基本上,元類的__new__在類屬性上循環以獲取屬性名稱,並將它們添加到options。你可以更直接一點,直接改變字段。下面是一個Python 2例如:

class Field(object): 
    def __init__(self, name=None): 
     self.name = name 
    def __str__(self): 
     if self.name: 
      return self.name 
     return type(self).__name__ 
    def __repr__(self): 
     return '%s(%s)' % (type(self).__name__, repr(self.name)) 

class MetaContainer(type): 
    @classmethod 
    def dub(metacls, name): 
     return name.replace('_', ' ').capitalize() 

    def __new__(cls, name, bases, attrs): 
     for attr in attrs: 
      if issubclass(type(attrs[attr]), Field) and attrs[attr].name is None: 
       attrs[attr].name = MetaContainer.dub(attr) 
     return super(MetaContainer, cls).__new__(cls, name, bases, attrs) 

class Container(object): 
    __metaclass__ = MetaContainer 
    first_name = Field() 
    foo = Field('Foobar') 

cntr = Container() 
cntr.first_name 

的Python 3幾乎相同,但您使用metaclass class argument*而非__metaclass__ property

class Container(object, metaclass=MetaContainer): 
    first_name = Field() 
    foo = Field('Foobar') 

可以編寫與metaclasses in in Python 2 and 3兼容版本直接使用元類創建容器的中間基類,而不是metaclass自變量或__metaclass__屬性:

ContainerBase = MetaContainer('ContainerBase', (object,), {}) 

class Container(ContainerBase): 
    first_name = Field() 
    foo = Field('Foobar') 

*針對變化的原因,見PEP 3115: Metaclasses in Python 3000

+0

謝謝!現在我終於也來到元類。但我想我已經看到了一種做我需要使用檢查模塊的方式 - 儘管我現在不需要。 – warvariuc