2010-06-16 56 views
12

我希望能夠創建一個自動「註冊」全局存儲庫(具有一些屬性)的類方法的python裝飾器。使用裝飾器的自動註冊類方法

示例代碼:

class my_class(object): 

    @register(prop1,prop2) 
    def my_method(arg1,arg2): 
     # method code here... 

    @register(prop3,prop4) 
    def my_other_method(arg1,arg2): 
     # method code here... 

我想,當加載完成後,地方會有包含字典:

{ "my_class.my_method"  : (prop1, prop2) 
    "my_class.my_other_method" : (prop3, prop4) } 

這可能嗎?

回答

15

不只是裝飾者,不是。但是元類可以在創建後自動處理類。如果您register裝飾只是讓有關元類應該做的筆記,你可以做到以下幾點:

registry = {} 

class RegisteringType(type): 
    def __init__(cls, name, bases, attrs): 
     for key, val in attrs.iteritems(): 
      properties = getattr(val, 'register', None) 
      if properties is not None: 
       registry['%s.%s' % (name, key)] = properties 

def register(*args): 
    def decorator(f): 
     f.register = tuple(args) 
     return f 
    return decorator 

class MyClass(object): 
    __metaclass__ = RegisteringType 
    @register('prop1','prop2') 
    def my_method(arg1,arg2): 
     pass 

    @register('prop3','prop4') 
    def my_other_method(arg1,arg2): 
     pass 

print registry 

打印

{'MyClass.my_other_method': ('prop3', 'prop4'), 'MyClass.my_method': ('prop1', 'prop2')} 
+0

+1這是要走的路(當你真的需要這樣的東西時) – 2010-06-16 15:17:56

+0

如果你有一個子類OurClass(MyClass) - 你想要做同樣的事情?你怎麼能讓__metaclass__遍歷基地和孩子的宿敵? – jMyles 2015-06-12 21:04:51

+0

@jMyles,父類的方法已經在註冊表中;你希望他們再次出現在孩子課上的方法?你有孩子課的基礎元組;您可以在進行子類註冊時(如果這就是您的意思)遍歷父/子類的任何或所有類字典以及新子類的屬性。 – 2015-06-13 13:46:13

1

否。裝飾器在它成爲方法之前接收函數,因此您不知道它在哪個類上。

+1

但是你可以編寫你自己的元類(或類裝飾器)來查看類的方法,並使用額外的'_register_properties'字段搜索方法,該字段之前由'register'裝飾器添加。請告訴我,如果你想要一個例子。 – tux21b 2010-06-16 15:03:37

2

不容易的,但如果你使用Python 3這應該工作:

registry = {} 
class MetaRegistry(type): 
    @classmethod 
    def __prepare__(mcl, name, bases): 
     def register(*props): 
      def deco(f): 
       registry[name + "." + f.__name__] = props 
       return f 
      return deco 
     d = dict() 
     d['register'] = register 
     return d 
    def __new__(mcl, name, bases, dct): 
     del dct['register'] 
     cls = super().__new__(mcl, name, bases, dct) 
     return cls 

class my_class(object, metaclass=MetaRegistry): 
    @register('prop1','prop2') 
    def my_method(arg1,arg2): 
     pass # method code here... 

    @register('prop3','prop4') 
    def my_other_method(arg1,arg2): 
     pass # method code here... 

print(registry) 

請注意,你不能等於在元類型類裝飾名稱方法名,因爲他們將被自動刪除通過元類的__new__方法中的del命令。

對於Python 2.6我認爲你將不得不明確告訴裝飾類的名稱使用。

12

下面是類裝飾一點點愛。我認爲這個語法比元類要簡單一些。

def class_register(cls): 
    cls._propdict = {} 
    for methodname in dir(cls): 
     method = getattr(cls, methodname) 
     if hasattr(method, '_prop'): 
      cls._propdict.update(
       {cls.__name__ + '.' + methodname: method._prop}) 
    return cls 


def register(*args): 
    def wrapper(func): 
     func._prop = args 
     return func 
    return wrapper 


@class_register 
class MyClass(object): 

    @register('prop1', 'prop2') 
    def my_method(self, arg1, arg2): 
     pass 

    @register('prop3', 'prop4') 
    def my_other_method(self, arg1, arg2): 
     pass 

myclass = MyClass() 
print(myclass._propdict) 
# {'MyClass.my_other_method': ('prop3', 'prop4'), 'MyClass.my_method': ('prop1', 'prop2')} 
+0

魔法,如果我見過它! – ThorSummoner 2017-11-02 07:22:45

+0

如果在另一個模塊中定義了「MyClass」,這是否工作? (假設模型導入'class_register') – DBCerigo 2018-01-26 17:26:00

2

不一樣美麗或優雅,但可能是最簡單的方式,如果你只需要在這個只有一個類:

_registry = {} 
class MyClass(object): 
    def register(*prop): 
     def decorator(meth): 
      _registry[MyClass.__name__ + '.' + meth.__name__] = prop 
     return decorator 

    @register('prop1', 'prop2') 
    def my_method(self, arg1, arg2): 
     pass 
    @register('prop3', 'prop4') 
    def my_other_method(self, arg1, arg2): 
     pass 

    del register 
5

如果您需要的類名稱,使用馬特的解決方案。但是,如果你真行只具有方法名稱 - 或方法的引用 - 在註冊表中,這可能是做這件事的一個簡單的方法:

class Registry: 
    r = {} 

    @classmethod 
    def register(cls, *args): 
     def decorator(fn): 
      cls.r[fn.__name__] = args 
      return fn 
     return decorator 

class MyClass(object): 

    @Registry.register("prop1","prop2") 
    def my_method(arg1,arg2): 
     pass 

    @Registry.register("prop3","prop4") 
    def my_other_method(arg1,arg2): 
     pass 

print Registry.r 

打印

{'my_other_method': ('prop3', 'prop4'), 'my_method': ('prop1', 'prop2')}