2013-07-12 28 views
1

我拉行作爲字典(使用SSDictCursor)一的參數集,並做一些處理,使用下面的方法一個namedtuple對象:創建僅利用MySQL數據庫通過

from collections import namedtuple 

class Foo(namedtuple('Foo', ['id', 'name', 'age'])): 
    __slots__ =() 

    def __init__(self, *args): 
     super(Foo, self).__init__(self, *args) 

    # ...some class methods below here 

class Bar(namedtuple('Bar', ['id', 'address', 'city', 'state']): 
    __slots__ =() 

    def __init__(self, *args): 
     super(Bar, self).__init__(self, *args) 

    # some class methods here... 

# more classes for distinct processing tasks... 

要使用namedtuple,我必須事先知道我想要的領域,這很好。不過,我希望允許用戶向我的程序中提供一條簡單的SELECT *語句,然後該語句將遍歷結果集的行,並使用這些不同的類執行多個任務。爲了做到這一點,我的班級必須以某種方式檢查從光標進入的N個字段,並只取對應於namedtuple定義所期望名稱的特定子集M < N.

我的第一個想法是嘗試編寫一個我可以應用於每個類的裝飾器,它將檢查類以查看它期望的字段,並將適當的參數傳遞給新對象。但是我在過去幾天剛剛開始閱讀裝修工,而且我對他們還沒有那麼自信。

所以我的問題是兩個部分:

  1. 這是可以做到用一個單一的裝飾,將找出哪些字段由特定類正在裝修需要的?
  2. 是否有替代具有相同的功能,將更容易使用,修改和理解?

我有太多的表和字段的潛在排列,每個結果集有數百萬行,只寫一個通用的namedtuple子類來處理每個不同的任務。查詢時間和可用內存已被證明是限制因素。

如果需要:

>>> sys.version 
'2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)]' 

回答

2

首先,你必須以定製namedtuple創建覆蓋__new__,因爲namedtuple__new__方法檢查它的參數之前,你甚至到達__init__。其次,如果您的目標是接受並過濾關鍵字參數,則需要採取**kwargs並過濾並通過,而不僅僅是*args

因此,將其組合在一起:

class Foo(namedtuple('Foo', ['id', 'name', 'age'])): 
    __slots__ =() 

    def __new__(cls, *args, **kwargs): 
     kwargs = {k: v for k, v in kwargs.items() if k in cls._fields} 
     return super(Foo, cls).__new__(cls, *args, **kwargs) 

您可以用itemgetter替換字典的理解,但我每次使用itemgetter有多個按鍵的時候,沒有人明白這意味着什麼,所以我不情願地停止使用它。


您還可以覆蓋__init__如果你有充分的理由這樣做,因爲這樣會盡快__new__返回一個Foo實例調用。

但是你不需要這樣做,因爲namedtuple的__init__沒有任何參數或做任何事情;值已經在__new__中設置(就像tuple和其他不可變類型一樣)。它看起來像CPython 2.7,你實際上可以super(Foo, self).__init__(*args, **kwargs)它只會被忽略,但與PyPy 1.9和CPython 3.3,你會得到一個TypeError。無論如何,沒有理由通過它們,並且沒有任何說明它應該起作用,所以即使在CPython 2.7中也不要這樣做。

請注意,您__init__將得到未過濾kwargs。如果你想改變這一點,你可以在__new__內部就地變異kwargs,而不是製作一本新字典。但我相信仍然不能保證做任何事情;它只是使其實現定義,無論您是獲取過濾參數還是未過濾,而不是保證未過濾。


那麼,你可以把它包起來嗎?當然!

def LenientNamedTuple(name, fields): 
    class Wrapper(namedtuple(name, fields)): 
     __slots__ =() 
     def __new__(cls, *args, **kwargs): 
      args = args[:len(fields)] 
      kwargs = {k: v for k, v in kwargs.items() if k in fields} 
      return super(Wrapper, cls).__new__(cls, *args, **kwargs) 
    return Wrapper 

注意這不必使用準私人/半記錄_fields類屬性的優勢,因爲我們已經有fields作爲參數。

另外,雖然我們在上面,但我添加了一條線來拋棄任何多餘的位置參數,正如評論中所建議的那樣。


現在,你只需要使用它,你會用namedtuple,它會自動忽略任何多餘的參數:

class Foo(LenientNamedTuple('Foo', ['id', 'name', 'age'])): 
    pass 

print(Foo(id=1, name=2, age=3, spam=4)) 

打印(美孚(1,2,3,4,5)) 打印(美孚(1,年齡= 3,名字= 2,雞蛋= 4))


我上傳a test,替換用的dict()字典理解上的genexpr 2.6 compatibil ity(2.6是namedtuple的最早版本),但沒有args截斷。它適用於CPython 2.6.7,2.7.2,2.7.5,3.2.3,3.3.0和3.3.1中的位置關鍵字和混合參數,包括亂序關鍵字,PyPy 1.9.0和2.0b1,以及Jython 2.7b。

+0

謝謝,我很欣賞細節。然而,當我們在'Wrapper'類中硬編碼時,我沒有看到如何用一組字段和'Bar'來聲明'Foo'和另一組字段? – Air

+0

經過一番嘗試,似乎可以與'class Wrapper(namedtuple(name,fields))'一起工作。不能可靠地採取args-only或混合args/kwargs,但這對我來說不是問題。如果該行只是一個混亂,請編輯,我會接受。 – Air

+0

@AirThomas:哪裏不能採用獨立或混合參數?我只是在每一個我擁有的Python上進行了測試,在每個我能想到的參數組合中都能夠測試,並且它總能按預期工作。請參閱編輯答案中的鏈接。 – abarnert

2

namedtuple類型具有屬性_fields其是在對象中的字段的名稱的一個元組。您可以使用它從數據庫記錄中挖出必需的字段。