2011-03-01 42 views
7

使用元類,我試圖通過簡化現有的實例方法來創建實例方法。問題是部分不適用於實例方法。這是我做的嘗試,以實現一個簡單的例子:使用Python中的partial創建元類中的實例方法3

from functools import partial 

class Aclass(object): 

    def __init__(self, value): 
     self._value = value 

    def complex(self, a, b):            
     return a + b + self._value 

class Atype(type): 

    def __new__(cls, name, bases, attrs): 
     return super(Atype, cls).__new__(cls, name, (Aclass,) + bases, attrs) 

    def __init__(cls, name, bases, attrs): 
     setattr(cls, 'simple', partial(cls.complex, b=1)) 

class B(metaclass=Atype): 
    pass 

b = B(10) 

print(b.complex(1, 2)) 
print(b.simple(1)) 

,輸出是:

13 
Traceback (most recent call last): 
    File "metatest.py", line 22, in <module> 
    print(b.simple(1)) 
TypeError: complex() takes exactly 3 non-keyword positional arguments (1 given) 

我已經使用lambda解決了改變:

setattr(cls, 'simple', partial(cls.complex, b=1)) 

到:

setattr(cls, 'simple', lambda self, x: cls.complex(self, x, b=1)) 

但它是醜陋的d有可選參數的問題。

我可以在實例__init__上創建這些方法,但我認爲這樣做更有意義,並且使用元類在類__init__上更有效。

任何想法如何正確地做到這一點?

+0

@martineau:我完全使用python-3.x標籤,但我沒有看到任何不使用「python」的理由。另外 - 不會「python3」成爲一個清潔標籤? – jsbueno 2011-03-02 01:15:08

+0

@jsbueno:同意 - 但它不會讓我將python-3.x更改爲python3。 – martineau 2011-03-02 01:54:55

+0

@jsbueno:標籤是python-3.x,我不知道(如果有的話)這些東西是決定的。我懷疑它可以在meta上進行討論,然後在有支持的情況下進行更改。無論如何python3是一個同義詞,所以你可以輸入它,它會被重新映射。 – 2011-03-02 08:10:04

回答

5

嗯,我對Python 3方法處理有點陌生 - 我能想到的最簡單的事情是重寫partial,以便它保留原始調用的第一個參數,然後插入「部分」參數。

它適用於您的示例,但它需要使用更復雜的模式進行測試。

from functools import wraps 

class Aclass(object): 
    def __init__(self, value): 
     self._value = value 

    def complex(self, a, b):            
     return a + b + self._value 

def repartial(func, *parameters, **kparms): 
    @wraps(func) 
    def wrapped(self, *args, **kw): 
     kw.update(kparms) 
     return func(self, *(args + parameters), **kw) 
    return wrapped 

class Atype(type): 
    def __new__(cls, name, bases, attrs): 
     return super(Atype, cls).__new__(cls, name, (Aclass,) + bases, attrs) 

    def __init__(cls, name, bases, attrs): 
     setattr(cls, 'simple', repartial(cls.complex, b=1)) 

class B(metaclass=Atype): 
    pass 

b = B(10) 

print(b.complex(1, 2)) 
print(b.simple(1)) 
+0

我認爲在'repartial()'中,'wrapper()'會更好,因爲在'repartial()'中調用'wrapped()'的嵌套函數的名稱更具描述性和正確性。只是一個非常好的答案挑選。 – martineau 2012-02-08 14:23:26

1

問題是由functools.partial()返回的對象是可調用的對象,而不是函數。所以顯然Python不關心在這種情況下嘗試像一個非功能的非功能。一種解決方法是創建一個函數作爲partial對象的包裝。

class Atype(type): 

    def __init__(cls, name, bases, attrs): 
     simple = partial(cls.complex, b=1) 
     setattr(cls, 'simple', lambda cls, a: simple(cls, a)) 

jsbueno的溶液(partial返回一個真正的函數重新實現)是好的,但。我真的不知道爲什麼functools.partial()不這樣工作;在這種情況下無法使用它是一個令人驚訝的缺陷。

+1

這是解決'部分'陷阱的好辦法,但我認爲我自己的答案更好;-),因爲,一方面,它具有較少的函數調用開銷。順便說一句,到目前爲止還沒有人提到的問題的另一個原因是'cls.complex'是一個未綁定的方法。 – martineau 2011-03-02 02:05:25

+1

在Python 3中,它不會顯示有未綁定的方法; 'Aclass.complex'是一個功能。 – kindall 2011-03-02 05:15:34

+1

有趣的是,當部分被賦予fset或fget屬性時,它就可以正常工作。 – Hernan 2011-03-02 22:06:41

3

而不是使用partial的,我只是定義Atype類是這樣的:

class Atype(type): 

    def __new__(cls, name, bases, attrs): 
     return super(Atype, cls).__new__(cls, name, (Aclass,) + bases, attrs) 

    def __init__(cls, name, bases, attrs): 
     def simple(self, a): 
      return cls.complex(self, a, 1) 
     setattr(cls, 'simple', simple) 

__init__()方法也可以更緊湊寫成:

def __init__(cls, name, bases, attrs): 
    setattr(cls, 'simple', lambda self, a: cls.complex(self, a, 1)) 
+0

是的,我真的不認爲元類(或'functools.partial()')是需要在這裏。也許這只是一個簡單的例子,但是,元類在user482819的實際應用中確實是需要的。 – kindall 2011-03-02 14:29:05

+0

的確,我需要根據元類的特定屬性動態地創建實例方法。 – Hernan 2011-03-02 19:21:28

相關問題