Ordered classes in Python 3

I am trying to use an "ordered class" as described in PEP 3115 (that is, a class whose members can be accessed in what they were declared). The implementation given there

# The custom dictionary class member_table(dict): def __init__(self): self.member_names = [] def __setitem__(self, key, value): # if the key is not already defined, add to the # list of keys. if key not in self: self.member_names.append(key) # Call superclass dict.__setitem__(self, key, value) # The metaclass class OrderedClass(type): # The prepare function @classmethod def __prepare__(metacls, name, bases): # No keywords in this case return member_table() # The metaclass invocation def __new__(cls, name, bases, classdict): # Note that we replace the classdict with a regular # dict before passing it to the superclass, so that we # don't continue to record member names after the class # has been created. result = type.__new__(cls, name, bases, dict(classdict)) result.member_names = classdict.member_names return result class MyClass(metaclass=OrderedClass): # method1 goes in array element 0 def method1(self): pass # method2 goes in array element 1 def method2(self): pass 

There are a few things that I am confused about. Firstly, is there a reason __prepare__ is a classmethod ? The definition does not use metacls - is it just a convention?

Secondly, when I try to execute this code, '__module__' ends with MyClass.member_names before 'method1' and 'method2' , which seems to contradict the comments that claim that 'method1' is the first element. Why does this special attribute get on the list while others are not? Are there others that might surprise me (other than __doc__ if the class has a docstring, and any one I define explicitly)?

Finally, this implementation does not extract member_names from the base classes. If I want to achieve this, is there something wrong with the next change to __prepare__ (except that it does not check for duplicates)?

 @classmethod def __prepare__(metacls, name, bases): prep_dict = member_table() for base in bases: try: prep_dict.member_names.extend(base.member_names) except AttributeError: pass return prep_dict 
+4
source share
1 answer

Firstly, is there a reason why __prepare__ is a class method? Definition does not use metacls - is it just a convention?

As pointed out in the comments, when __prepare__ is called, the class itself has not yet been created. This means that if this is a regular method (with self as the first parameter) - there will be no value in this call to become self .

And this makes sense, because the fact that __prepare__ is to return an object of type dict, which will be used instead of the usual dict in parsing the body of the class, is therefore not dependent on the class being created at all.

If, as mentioned in the comments, it was a staticmethod (which means that it will not receive the first metacls parameter), then __prepare__ will not be able to access any other metaclass methods - this is not necessarily a limitation.

And yes, just like self , metacls in this case is just a convention - the usual Python identifier. (Note that when class methods are used in regular classes, the first parameter is usually indicated by cls instead of metacls .)

Secondly, when I try to execute this code, __module__ ends in MyClass.member_names before method1 and method2 , which seems to contradict the comments that claim that method1 is the first element. Why does this special attribute get on the list while others are not? Are there others that might surprise me (other than __doc__ if the class has a docstring, and any one I define explicitly)?

Probably because the special __module__ element for classes was thought up after they thought of the __prepare__ method for metaclasses. (I could not check on Google for the PEP defining __module__ , but I would say that it is.)

And no, there is no guarantee that future versions of Python will not add a few more magic attributes to classes that may appear in front of your explicit class members in the dictionary.

But with metaclasses, you have complete control - you can simply set up your object of type dict ( member_table in your code) to not count any attributes starting with "__" - for example. Or even not adding to the final class instance at all (at the risk of your classes defining this method without working with certain Python functions).

Finally, this implementation does not extract member elements from base classes. If I want to achieve this, is there something wrong with the next change to __prepare__ ?

Reading it, I see nothing wrong with your proposed implementation, but of course you will have to test it.

Update (2013-06-30): this issue is being actively discussed in the list of Python developers, and it looks like from Python 3.4, all classes will be ordered by default, without the need to use a metaclass or __prepare__ for order only

+5
source

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


All Articles