2012-12-18 77 views
13

很多時候,我發現自己編碼瑣碎的數據類型一樣的Python:快速和骯髒的數據類型(DTO)

def Pruefer: 
    def __init__(self, ident, maxNum=float('inf'), name=""): 
     self.ident = ident 
     self.maxNum = maxNum 
     self.name = name 

雖然這是非常有用的(顯然我不希望匿名的三元組來代替上述),這也是非常模板化的。現在,例如,當我想用​​類的字典,我要補充更多的樣板像

def __hash__(self): 
     return hash(self.ident, self.maxNum, self.name) 

我承認,這可能是難以識別我所有的樣板課之間的通用模式,但無論如何,我'd喜歡這個問題:**在python中是否有任何 流行習慣用來派生具有命名訪問器的快速和髒數據類型? **或者,如果沒有,也許一個Python大師可能想炫耀一些元類黑客或類工廠,讓我的生活更輕鬆?

+2

我覺得'namedtuple'已經足夠好了(用代碼示例添加完整答案) –

回答

20
>>> from collections import namedtuple 
>>> Pruefer = namedtuple("Pruefer", "ident maxNum name") 
>>> pr = Pruefer(1,2,3) 
>>> pr.ident 
1 
>>> pr.maxNum 
2 
>>> pr.name 
3 
>>> hash(pr) 
2528502973977326415 

提供的默認值,你需要做的多一點...簡單的解決辦法是寫與重新定義子類__new__方法:

>>> class Pruefer(namedtuple("Pruefer", "ident maxNum name")): 
...  def __new__(cls, ident, maxNum=float('inf'), name=""): 
...   return super(Pruefer, cls).__new__(cls, ident, maxNum, name) 
... 
>>> Pruefer(1) 
Pruefer(ident=1, maxNum=inf, name='') 
+0

非常好!你碰巧也知道一些讓我具有默認值的東西嗎? –

+1

@JoSo - 你可以有一個工廠函數,它有默認值並返回一個Pruefer實例。 – mgilson

+0

這可能只是我,但我更喜歡'(「ident」,「maxNum」,「name」)'以空格分隔的字符串的版本......對我而言,這似乎更明顯地發生了什麼。 – mgilson

1

我沒有太多的添加到由阿列克謝Kachayev已經出色答卷 - 但是,有一點是可以有用的是以下模式:

Pruefer.__new__.func_defaults = (1,float('inf'),"") 

這將允許您創建一個工廠函數返回一個新的命名元組可以有默認參數:

def default_named_tuple(name,args,defaults=None): 
    named_tuple = collections.namedtuple(name,args) 
    if defaults is not None: 
     named_tuple.__new__.func_defaults = defaults 
    return named_tuple 

這可能看起來像黑魔法 - 它確實對我來說是第一次,但它在Data Model所有記錄並在this post中進行了討論。

在行動:

>>> default_named_tuple("Pruefer", "ident maxNum name",(1,float('inf'),'')) 
<class '__main__.Pruefer'> 
>>> Pruefer = default_named_tuple("Pruefer", "ident maxNum name",(1,float('inf'),'')) 
>>> Pruefer() 
Pruefer(ident=1, maxNum=inf, name='') 
>>> Pruefer(3) 
Pruefer(ident=3, maxNum=inf, name='') 
>>> Pruefer(3,10050) 
Pruefer(ident=3, maxNum=10050, name='') 
>>> Pruefer(3,10050,"cowhide") 
Pruefer(ident=3, maxNum=10050, name='cowhide') 
>>> Pruefer(maxNum=12) 
Pruefer(ident=1, maxNum=12, name='') 

而且只有指定的某些參數的默認值:

>>> Pruefer = default_named_tuple("Pruefer", "ident maxNum name",(float('inf'),'')) 
>>> Pruefer(maxNum=12) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __new__() takes at least 2 arguments (2 given) 
>>> Pruefer(1,maxNum=12) 
Pruefer(ident=1, maxNum=12, name='') 

需要注意的是書面,這可能是唯一的安全在傳遞一個tupledefaults。但是,通過確保在函數中有合理的tuple對象,您可以輕鬆獲得更多花式。

1

另一種方法可能會幫助你讓你的鍋爐代碼更通用一點就是迭代(局部)變量字典。這使您可以將變量放在一個列表中,並在循環中處理這些變量。 E.g:

class Pruefer: 
    def __init__(self, ident, maxNum=float('inf'), name=""): 
     for n in "ident maxNum name".split(): 
      v = locals()[n] # extract value from local variables 
      setattr(self, n, v) # set member variable 

    def printMemberVars(self): 
     print("Member variables are:") 
     for k,v in vars(self).items(): 
      print(" {}: '{}'".format(k, v)) 


P = Pruefer("Id", 100, "John") 
P.printMemberVars() 

給出:

Member Variables are: 
    ident: 'Id' 
    maxNum: '100' 
    name: 'John' 

從資源的有效利用的角度來看,這種做法當然是不理想的。

+0

元類的好候選者可以刪除更多的鍋爐板... – martineau

1

Python 3.6中最有希望的事情之一是變量註釋。它們允許在接下來的方式來定義namedtuple爲類:

In [1]: from typing import NamedTuple 

In [2]: class Pruefer(NamedTuple): 
    ...:  ident: int 
    ...:  max_num: int 
    ...:  name: str 
    ...:  

In [3]: Pruefer(1,4,"name") 
Out[3]: Pruefer(ident=1, max_num=4, name='name') 

它等同於namedtuple,但節省了註解,並允許一些靜態類型分析儀像mypy檢查類型。

相關問題