In Python2.7 this code can work very well, __getattr__
in MetaTable
will run. But in Python 3 it doesn't work.
class MetaTable(type): def __getattr__(cls, key): temp = key.split("__") name = temp[0] alias = None if len(temp) > 1: alias = temp[1] return cls(name, alias) class Table(object): __metaclass__ = MetaTable def __init__(self, name, alias=None): self._name = name self._alias = alias d = Table d.student__s
But in Python 3.5 I get an attribute error instead:
Traceback (most recent call last): File "/Users/wyx/project/python3/sql/dd.py", line 31, in <module> d.student__s AttributeError: type object 'Table' has no attribute 'student__s'
A metaclass in Python is a class of a class that defines how a class behaves. A class is itself an instance of a metaclass. A class in Python defines how the instance of the class will behave. In order to understand metaclasses well, one needs to have prior experience working with Python classes.
Python provides a __bases__ attribute on each class that can be used to obtain a list of classes the given class inherits. The __bases__ property of the class contains a list of all the base classes that the given class inherits.
In order to set metaclass of a class, we use the __metaclass__ attribute. Metaclasses are used at the time the class is defined, so setting it explicitly after the class definition has no effect. The best idea I can think of is to re-define whole class and add the __metaclass__ attribute dynamically somehow.
type is a metaclass, of which classes are instances. Just as an ordinary object is an instance of a class, any new-style class in Python, and thus any class in Python 3, is an instance of the type metaclass.
Python 3 changed how you specify a metaclass, __metaclass__
is no longer checked.
Use metaclass=...
in the class signature:
class Table(object, metaclass=MetaTable):
Demo:
>>> class MetaTable(type): ... def __getattr__(cls, key): ... temp = key.split("__") ... name = temp[0] ... alias = None ... if len(temp) > 1: ... alias = temp[1] ... return cls(name, alias) ... >>> class Table(object, metaclass=MetaTable): ... def __init__(self, name, alias=None): ... self._name = name ... self._alias = alias ... >>> d = Table >>> d.student__s <__main__.Table object at 0x10d7b56a0>
If you need to provide support for both Python 2 and 3 in your codebase, you can use the six.with_metaclass()
baseclass generator or the @six.add_metaclass()
class decorator to specify the metaclass.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With