Metadata
Metaphysics
Metaprogramming
- Duck typing
- Decorators
- Metaclasses
If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.
class Duck:
def quack(self):
print("Quack!")
class Panda:
def quack(self):
print("Panda quack!")
def duck_duck_go(obj):
obj.quack()
duck_duck_go(Duck()) # Works
duck_duck_go(Panda()) # Works
duck_duck_go(1) # AE: 'int' object has no attribute 'quack'
def duck_duck_go(obj):
if hasattr(obj, 'quack'):
obj.quack()
duck_duck_go(Duck()) # Works
duck_duck_go(Panda()) # Works
duck_duck_go(1) # Nothing happens
class Person:
def __init__(self, name):
self.quack = name
duck_duck_go(Person('Ivo'))
# TypeError: 'str' object is not callable
class Panda:
pass
def create_panda(attributes):
panda = Panda()
for key, value in attributes.items():
setattr(panda, key, value)
return panda
p = create_panda({'name': 'Ivo', 'age': 23, 'weight': 80})
print(p.__dict__) # {'name': 'Ivo', 'age': 23, 'weight': 80}
p = create_panda({'name': 'Ivo', 'weight': 80})
print(p.__dict__) # {'name': 'Ivo', 'weight': 80}
def duck_duck_go(obj):
if hasattr(obj, 'quack')\
and callable(getattr(obj, 'quack')):
obj.quack()
def debug(func):
fname = func.__qualname__
@wraps(func)
def wrapper(*arg, **kwargs):
print('Calling {}'.format(fname))
return func(*arg, **kwargs)
return wrapper
class Panda:
@debug
def be_panda(self):
print('Being panda')
@debug
def awesome(self):
print('Pandas are awesome!')
@debugmethods
class Panda:
def be_panda(self):
print('Being panda')
def awesome(self):
print('Pandas are awesome!')
def debugmethods(cls):
return cls
def debugmethods(cls):
for attr, value in vars(cls).items():
if callable(value):
setattr(cls, attr, debug(value))
return cls
@debugmethods
class Panda:
def be_panda(self):
print('Being panda')
def awesome(self):
print('Pandas are awesome!')
p = Panda()
p.be_panda() # prints 'Calling be_panda'
p.awesome() # prints 'Calling awesome'
@debugmethods
class Panda:
pass
@debugmethods
class Person:
pass
@debugmethods
class Task:
pass
# ...
>>> type(5)
<class 'int'>
>>> type('python')
<class 'str'>
>>> class Panda: pass
...
>>> p = Panda()
>>> type(p)
<class '__main__.Panda'>
>>> class Panda: pass
...
>>> p = Panda()
>>> type(p)
<class '__main__.Panda'>
>>> int
<class 'int'>
>>> str
<class 'str'>
>>> Panda
<class '__main__.Panda'>
>>> type(int)
<class 'type'>
>>> type(str)
<class 'type'>
>>> type(Panda)
<class 'type'>
>>> type(type)
<class 'type'>
class type:
...
Panda.__dict__ == vars(Panda)
p = Panda()
p.__dict__ == vars(p)
module.__dict__ == vars(module)
class Panda:
def __init__(self, name):
self.name = name
def be_panda(self):
print('Being panda')
body = '''
def __init__(self, name):
self.name = name
def be_panda(self):
print('Bamboo & sleep')
'''
clsname = 'Panda'
bases = (object, )
clsdict = type.__prepare__(clsname, bases)
exec(body, globals(), clsdict)
Panda = type(clsname, bases, clsdict)
body = '''
def __init__(self, name):
self.name = name
def be_panda(self):
print('Bamboo & sleep')
'''
clsname = 'Panda'
bases = (object, )
clsdict = type.__prepare__(clsname, bases)
exec(body, globals(), clsdict)
Panda = type(clsname, bases, clsdict)
p = Panda('Ivo')
print(p.name)
p.be_panda()
class Panda(metaclass=type):
def __init__(self, name):
self.name = name
def be_panda(self):
print('Being panda')
class mytype(type):
def __new__(cls, name, bases, clsdict):
clsobj = super().__new__(cls, name, bases, clsdict)
print('Constructed new type')
return clsobj
class Panda(metaclass=mytype):
pass
p = Panda() # Prints 'Constructed a new type'
@debugmethods
class Panda:
pass
@debugmethods
class Person:
pass
@debugmethods
class Task:
pass
# ...
class debugmeta(type):
def __new__(cls, clsname, bases, clsdict):
clsobj = super().__new__(cls, clsname, bases, clsdict)
# Decoration here.
clsobj = debugmethods(clsobj)
return clsobj
class Base(metaclass=debugmeta):
pass
class Panda(Base):
pass
class Person(Base):
pass
class Task(Base):
pass
class no_multiple_inheritence(type):
def __new__(cls, name, bases, clsdict):
if len(bases) > 1:
raise TypeError('No multiple inheritence!')
return super().__new__(cls, name, bases, clsdict)
class Base(metaclass=no_multiple_inheritence):
pass
class A(Base):
pass
class B(Base):
pass
class C(A, B): # Raises error
pass