class MyClass(object):
pass
MyClass # __main__.MyClass
MyClass.__class__ # type
type(MyClass()) # __main__.MyClass
type(MyClass) # type
type(object) # type
type(type) # type
>>> print(type.__doc__)
type(object_or_name, bases, dict)
type(object) -> the object's type
type(name, bases, dict) -> a new type
# class parent class
# name classes __dict__
type( 'MyClass', (), {} ) # returns __main__.MyClass
Klass = type('Klass', (), {})
class Klass(object):
pass
Klass2 = type('Klass2', (Klass, ), {})
class Klass2(Klass):
pass
Klass3 = type('Klass3', (Klass, Klass2), {'a': 42})
class Klass3(Klass, Klass2):
a = 42
Klass4 = type(
'Klass4',
(),
{'a': 42, 'method': lambda self, num: self.a + num}
)
class Klass4(object):
a = 42
def method(self, x):
return self.a + x
Metaclass.__prepare__(mcls, name, bases) # classmethod
Metaclass.__new__(mcls, name, bases, attrs, **kwargs)
Metaclass.__init__(cls, name, bases, attrs, **kwargs)
Metaclass.__call__(cls, *args, **kwargs)
__prepare__
__new__
returnsclass OnlyUppercase(dict):
def __setitem__(self, key, value):
if isinstance(key, str) and key.isupper():
super().__setitem__(key, value)
class MyMeta(type):
@classmethod
def __prepare__(mcls, name, bases):
return OnlyUppercase()
def __new__(mcls, name, bases, attrs):
return super().__new__(mcls, name, bases, dict(attrs))
class MyClass(metaclass=MyMeta):
lowercase = 1
UPPERCASE = 2
MyClass.__dict__ # {'UPPERCASE': 2}
__new__
class MyClass ...
-> __new__
runs)__init__
__new__
)__call__
Class.__new__
& object.__init__
)class MyMeta(type):
def __call__(cls, *args, **kwargs):
print('In metaclass', args, kwargs)
return super().__call__(*args, **kwargs)
class MyClass(metaclass=MyMeta):
def __init__(cls, *args, **kwargs):
print('In class', args, kwargs)
>>> obj = MyClass(1, 2, foo=42)
In metaclass (1, 2), {'foo': 42}
In class (1, 2) {'foo': 42}
class MyMeta(type):
@classmethod
def __prepare__(mcls, name, bases):
print('Meta __prepare__')
return super().__prepare__(mcls, name, bases)
def __new__(mcls, name, bases, attrs):
print('Meta __new__')
return super().__new__(mcls, name, bases, attrs)
def __init__(cls, name, bases, attrs):
print('Meta __init__')
return super().__init__(name, bases, attrs)
def __call__(cls, *args, **kwargs):
print('Meta __call__')
return super().__call__(*args, **kwargs)
>>> class MyClass(metaclass=MyMeta):
... def __new__(cls, *args, **kwargs):
... print('Class __new__')
... return super().__new__(cls)
... def __init__(self, *args, **kwargs):
... print('Class __init__')
...
Meta __prepare__
Meta __new__
Meta __init__
>>>
>>> obj = MyClass()
Meta __call__
Class __new__
Class __init__
class SingletonMeta(type):
def __call__(cls, *args, **kwargs):
if not hasattr(cls, '_inst'):
obj = super(SingletonMeta, cls).__call__(*args, **kwargs)
cls._inst = obj
return cls._inst
class MyClass(metaclass=SingletonMeta):
pass
>>> a = MyClass()
>>> b = MyClass()
>>> a is b
True
>>> class MyClass(metaclass=print):
... a = 1
...
MyClass () {'__qualname__': 'MyClass', '__module__': '__main__', 'a': 1}
>>> MyClass is None
True
Class.__dict__
and attr is data descriptor -> Class.__dict__['attr'].__get__(instance, Class)
instance.__dict__
-> instance.__dict__['attr']
Class.__dict__
and attr is not a data descriptor -> Class.__dict__['attr'].__get__(instance, Class)
Class.__dict__
-> Class.__dict__['attr']
Class.__getattr__
exists -> Class.__getattr__('attr')
Metaclass.__dict__
and attr is data desciptor -> Metaclass.__dict__['attr'].__get__(Class, Metaclass)
Class.__dict__
and attr is descriptor -> Class.__dict__['attr'].__get__(None, Class)
Class.__dict__
-> Class.__dict__['attr']
Metaclass.__dict__
and attr is not a data descriptor -> Metaclass.__dict__['attr'].__get__(Class, Metaclass)
Metaclass.__dict__
-> Metaclass.__dict__['attr']
Metaclass.__getattr__
exists -> Metaclass.__getattr__('attr')
__init__
etc.)__get__
, __set__
& __delete__
methods__get__
& __set__
= data descriptors__get__
= non-data descriptorsproperty
decorator (getter & setter)descr.__get__(self, obj, cls) # -> value
descr.__set__(self, obj, value) # -> None
descr.__delete__(self, obj) # -> None
class Descriptor(object):
def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name
def __get__(self, obj, cls):
print('get', self.name)
return self.val
def __set__(self, obj, val):
print('set', self.name)
self.val = val
class MyClass(object):
attr = Descriptor(initval=10, name='attr')
>>> MyClass.attr
get attr
10
>>> MyClass.attr = 11
>>> MyClass.attr
11
>>> # oops
>>> a = MyClass()
>>> a.attr
get attr
10
>>> a.attr = 11
set attr
>>> a.attr
get attr
11
>>> b = MyClass()
>>> b.attr
get attr
11
>>> # wat
instance.__dict__
class Descriptor(object):
def __init__(self, name):
self.name = name
def __get__(self, obj, cls):
if obj is None:
return self
try:
print('get', self.name)
return obj.__dict__[self.name]
except KeyError:
raise AttributeError()
def __set__(self, obj, val):
print('set', self.name)
obj.__dict__[self.name] = val
class MyClass(object):
attr = Descriptor('attr')
>>> MyClass.attr
<__main__.Descriptor at 0x312....>
>>>
>>> a = MyClass()
>>> a.attr
get attr
Traceback (most recent call last)
....
AttributeError: ...
>>> a.attr = 1
set attr
>>> a.attr
get attr
1
>>>
>>> b = MyClass()
>>> b.attr = 2
set attr
>>> b.attr
get attr
2
>>> a.attr
get attr
1
attr = Descriptor('attr')
-> attr = Descriptor()
class Descriptor(object):
def __init__(self):
self.name = None
def __get__(self, obj, cls):
if obj is None:
return self
try:
print('get', self.name)
return obj.__dict__[self.name]
except KeyError:
raise AttributeError()
def __set__(self, obj, val):
print('set', self.name)
obj.__dict__[self.name] = val
class MyMeta(type):
def __new__(mcls, name, bases, attrs):
for k, v in attrs.items():
if isinstance(v, Descriptor):
v.name = k
return super().__new__(mcls, name, bases, attrs)
class MyClass(metaclass=MyMeta):
attr = Descriptor()
>>> MyClass.attr
<__main__.Descriptor at 0x312....>
>>>
>>> a = MyClass()
>>> a.attr = 1
set attr
>>> a.attr
get attr
1
>>> a.__dict__
{'attr': 1}
>>> class MyClass:
... a: int
... b: str
...
>>> MyClass.__annotations__
{'a': int, 'b': str}
>>> class MyClass(Typed):
... a: int
... b: str
...
>>> obj = MyClass()
>>> obj.a = 1
>>> obj.a = 'foo'
...
...
TypeError: 'foo' is not of type 'int'
class TypedDescriptor:
def __init__(self, name, tp):
self.name = name
self.tp = tp
def __get__(self, obj, cls):
if obj is None:
return self
try:
return obj.__dict__[self.name]
except KeyError:
raise AttributeError()
def __set__(self, obj, val):
if not isinstance(val, self.tp):
raise TypeError()
obj.__dict__[self.name] = val
class TypedMeta(type):
def __new__(mcls, name, bases, attrs):
ann = attrs.get('__annotations__', {})
for k, v in ann.items():
attrs[k] = TypedDescriptor(name=k, tp=v)
return super().__new__(mcls, name, bases, attrs)
class Typed(metaclass=TypedMeta):
pass