2010-06-16 30 views
12

Tôi muốn có thể tạo trình trang trí python tự động "đăng ký" các phương thức lớp trong kho lưu trữ toàn cục (với một số thuộc tính).Tự động đăng ký các phương thức lớp bằng cách sử dụng trang trí

Ví dụ mã:

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... 

Tôi muốn rằng khi tải xong, ở đâu đó sẽ có một dict chứa:

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

Đây có phải là có thể?

Trả lời

15

Không chỉ với một người trang trí, không. Nhưng một metaclass có thể tự động làm việc với một class sau khi nó được tạo ra. Nếu register trang trí của bạn chỉ làm cho ghi chú về những gì metaclass nên làm, bạn có thể làm như sau:

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 

in

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

+1 đây là cách để đi (khi bạn thực sự cần một cái gì đó như thế này) –

+0

Nếu bạn có một lớp con, OurClass (MyClass) - và bạn muốn làm điều tương tự? Làm thế nào bạn có thể nhận được __metaclass__ để đi qua các attrs của cả hai cơ sở và trẻ em? – jMyles

+0

@jMyles, Các phương thức của lớp cha đã có trong sổ đăng ký; bạn muốn chúng hiển thị lại như là các phương pháp trên lớp con?Bạn có tuple cơ sở cho lớp con; bạn có thể lặp qua bất kỳ hoặc tất cả các từ điển lớp của lớp cha/cơ sở, cũng như các thuộc tính của lớp con mới, khi thực hiện đăng ký lớp con (nếu đó là ý của bạn). –

1

No. Người trang trí sẽ nhận được chức năng trước khi nó trở thành một phương pháp, vì vậy bạn không biết nó đang ở trên lớp nào.

+1

Nhưng bạn có thể viết metaclass của riêng bạn (hoặc lớp trang trí) để xem xét các phương thức của lớp và tìm kiếm các phương thức với trường '_register_properties' bổ sung, được thêm vào bởi trang trí' register' trước đây. Hãy cho tôi biết, nếu bạn muốn có một ví dụ về điều đó. – tux21b

2

Không dễ dàng, nhưng nếu bạn đang sử dụng Python 3 này nên làm việc:

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) 

Lưu ý rằng bạn không thể có tên phương pháp tương đương với tên trang trí trong lớp meta-gõ, vì họ sẽ được tự động xóa bởi lệnh del trong phương thức __new__ của metaclass.

Đối với Python 2.6 Tôi nghĩ bạn sẽ phải nói rõ ràng cho người trang trí tên lớp để sử dụng.

12

Dưới đây là một tình yêu nhỏ cho trang trí lớp học. Tôi nghĩ cú pháp hơi đơn giản hơn so với yêu cầu đối với metaclasses.

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

wizard nếu tôi đã từng nhìn thấy nó! – ThorSummoner

+0

Tính năng này có hoạt động nếu 'MyClass' được định nghĩa trong mô-đun khác không? (giả sử rằng mô hình nhập khẩu 'class_register') – DBCerigo

2

Không như đẹp hay thanh lịch, nhưng có lẽ cách đơn giản nhất nếu bạn chỉ cần điều này trong chỉ có một lớp:

_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

Nếu bạn cần tên lớp, sử dụng giải pháp của Matt. Tuy nhiên, nếu bạn là ok với chỉ có tên các phương pháp - hoặc một tham chiếu đến phương pháp này - trong registry, đây có thể là một cách đơn giản hơn để làm việc đó:

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 

in

{'my_other_method': ('prop3', 'prop4'), 'my_method': ('prop1', 'prop2')} 
Các vấn đề liên quan