Reputation: 28
I'm learning metaclass programming in python 3, but I have some problems
class UpperAttrMetaClass(type): # to uppercase all attrs
def __new__(mcs, class_name, class_parents, class_attr):
attrs = ((name, value) for name, value in class_attr.items() if not
name.startswith('__'))
uppercase_attrs = dict((name.upper(), value) for name, value in attrs)
return super(UpperAttrMetaClass, mcs).__new__(mcs, class_name,
class_parents, uppercase_attrs)
class Base(metaclass=UpperAttrMetaClass):
bar = 12
def __init__(self, params):
super(Base, self).__init__()
self.params = params
t = Base(1)
print(t.BAR)
print(t.params)
This code can uppercase all attrs.
I want to pass a parameter to init, but when I run this code, I'm prompted to make a mistake that
TypeError: object() takes no parameters
How can I solve this problem?
Upvotes: 0
Views: 663
Reputation: 1121784
You are filtering out the __init__
method:
attrs = ((name, value) for name, value in class_attr.items() if not
name.startswith('__'))
attrs
are all the attributes that do not start with __
. You then uppercase those attrs
and use those for the class you create, so __init__
is never used for the new class. Because the resulting Bar
class has no __init__
method, object.__init__
is used, and that method doesn't take parameters:
>>> sorted(vars(Base))
['BAR', '__dict__', '__doc__', '__module__', '__weakref__']
>>> Base.__init__
<slot wrapper '__init__' of 'object' objects>
Include all attributes, do not filter, but only uppercase those without __
:
class UpperAttrMetaClass(type): # to uppercase all attrs
def __new__(mcs, class_name, class_parents, class_attr):
attrs = {name if name.startswith('__') else name.upper(): value for name, value in class_attr.items()}
return super().__new__(mcs, class_name, class_parents, attrs)
I used a dictionary comprehension here; note the name if name.startswith('__') else name.upper()
conditional expression, which produces an uppercase attribute name when the name does not start with __
.
I also used the 0-argument form of super()
, this is Python 3 after all.
Now the metaclass works correctly and Base.__init__
exists:
>>> sorted(vars(Base))
['BAR', '__dict__', '__doc__', '__init__', '__module__', '__weakref__']
>>> t = Base(1)
>>> t.BAR
12
>>> t.params
1
Upvotes: 1