Python decoder to change variable in current scope

Purpose: create a decorator that can change the area in which it is used.

If it worked:

class Blah(): # or perhaps class Blah(ParentClassWhichMakesThisPossible) def one(self): pass @decorated def two(self): pass >>> Blah.decorated ["two"] 

Why? I essentially want to write classes that can support certain method dictionaries so that I can get lists of available methods of different types for each class. uh .....

I want to do this:

 class RuleClass(ParentClass): @rule def blah(self): pass @rule def kapow(self): pass def shazam(self): class OtherRuleClass(ParentClass): @rule def foo(self): pass def bar(self): pass >>> RuleClass.rules.keys() ["blah", "kapow"] >>> OtherRuleClass.rules.keys() ["foo"] 
+4
source share
2 answers

You can do what you want with the class decorator (in Python 2.6) or the metaclass. Class Decorator Version:

 def rule(f): f.rule = True return f def getRules(cls): cls.rules = {} for attr, value in cls.__dict__.iteritems(): if getattr(value, 'rule', False): cls.rules[attr] = value return cls @getRules class RuleClass: @rule def foo(self): pass 

Metaclass Version:

 def rule(f): f.rule = True return f class RuleType(type): def __init__(self, name, bases, attrs): self.rules = {} for attr, value in attrs.iteritems(): if getattr(value, 'rule', False): self.rules[attr] = value super(RuleType, self).__init__(name, bases, attrs) class RuleBase(object): __metaclass__ = RuleType class RuleClass(RuleBase): @rule def foo(self): pass 

Note that none of them do what you ask for (modify the calling namespace) because it is fragile, difficult, and often impossible. Instead, they both process the class β€” through the class’s decorator or the __init__ metaclass method β€” by checking all attributes and populating the rules attribute. The difference between the two is that the metaclass solution works in Python 2.5 and earlier (prior to 2.2) and that the metaclass is inherited. With the decorator, subclasses should each apply the decorator separately (if they want to set the rule attribute.)

Both solutions do not take into account inheritance - they do not look at the parent class when searching for methods marked as rules, and do not look at the rules parent class. It is not difficult to expand to do this if that is what you want.

+9
source

The problem is that at the moment the decorated decoder is called, there is no Blah object yet: the class object is built after the class body completes execution. The easiest way to have decorated to store information "somewhere else", for example. function attribute, then the last pass (decorator or class metaclass) retrieves this information in the desired dictionary.

Class decorators are simpler, but they are not inherited (therefore, they will not come from the parent class), while metaclasses are inherited - therefore, if you insist on inheritance, it should be a metaclass. The simplest is, firstly, with the class decorator and the "list" that you have at the beginning of your Q, and not with the "dict" option that you have later:

 import inspect def classdecorator(aclass): decorated = [] for name, value in inspect.getmembers(aclass, inspect.ismethod): if hasattr(value, '_decorated'): decorated.append(name) del value._decorated aclass.decorated = decorated return aclass def decorated(afun): afun._decorated = True return afun 

Now,

 @classdecorator class Blah(object): def one(self): pass @decorated def two(self): pass 

gives you the Blah.decorated list that you request in the first part of your Q. Creating instead a dict, as you request in the second part of your Q, simply means changing decorated.append(name) to decorated[name] = value in the above the code, and, of course, the initialization of decorated in the class decorator to an empty dict, not an empty list.

A metaclass variant will use the __init__ metaclass to perform essentially the same post-processing after the class body is built - the __init__ metaclass receives the dict corresponding to the class body as the last argument (but you, you will have to maintain the inheritance yourself, corresponding dealing with any base class similar to a dict or list). Thus, the metaclass approach in practice is only β€œa little more complicated than the cool decorator, but conceptually it was much more complicated for most people. I will give all the details for the metaclass if you need them, but I will recommend sticking with a simpler class decorator if it is perhaps.

+4
source

Source: https://habr.com/ru/post/1308031/


All Articles