2.7. Dynamic Typing
2.7.1. Duck typing
Syntax similarities:
data = {1}
isinstance(data, set) # True
isinstance(data, dict) # False
data = {1: 1}
isinstance(data, set) # False
isinstance(data, dict) # True
data = {}
isinstance(data, set) # False
isinstance(data, dict) # True
data = {1:1}
type(data)
# <class 'dict'>
data
# {1:1}
_ = data.pop(1)
type(data)
# <class 'dict'>
data
# {}
data = {1}
type(data)
# <class 'set'>
data
# {1}
_ = data.pop()
type(data)
# <class 'set'>
data
# set()
2.7.2. Everything is an object
even function is an object!
2.7.3. Object properties
def add_numbers(a: int, b: float) -> float:
"""Function add numbers"""
return a + b
print(add_numbers.__doc__)
# Function add numbers
print(add_numbers.__name__)
# add_numbers
print(add_numbers.__annotations__)
# {'a': <class 'int'>, 'b': <class 'float'>, 'return': <class 'float'>}
print(add_numbers.__class__)
# <class 'function'>
2.7.4. Object methods
def add_numbers(a, b):
"""Function add numbers"""
return a + b
add_numbers(1, 2)
# 3
add_numbers.__call__(1, 2)
# 3
add_numbers()
# Traceback (most recent call last):
# TypeError: function() missing 2 required positional arguments: 'a' and 'b'
add_numbers.__call__()
# Traceback (most recent call last):
# TypeError: function() missing 2 required positional arguments: 'a' and 'b'
2.7.5. Injecting properties
def add_numbers(a, b):
"""Function add numbers"""
return a + b
add_numbers.myattr = 10
print(add_numbers.myattr)
# 10
2.7.6. Injecting methods
def add_numbers(a, b):
"""Function add numbers"""
return a + b
add_numbers.say_hello = lambda name: print(f'My name... {name}')
add_numbers.say_hello('José Jiménez')
# My name... José Jiménez
2.7.7. Proxy methods
One of the most common use of *args
, **kwargs
is for proxy methods:
class Point2D:
def __init__(self, x, y):
self.x = x
self.y = y
class Point3D(Point2D):
def __init__(self, *args, **kwargs):
if 'z' in kwargs:
z = kwargs.pop('z')
else:
*args, z = args
super().__init__(*args, **kwargs)
self.z = z
def __str__(self):
return f'Point3D(x={self.x}, y={self.y}, z={self.z})'
p1 = Point3D(x=1, y=2, z=3)
p2 = Point3D(1, 2, 3)
p3 = Point3D(1, 2, z=3)
print(p1)
# Point3D(x=1, y=2, z=3)
print(p2)
# Point3D(x=1, y=2, z=3)
print(p3)
# Point3D(x=1, y=2, z=3)
2.7.8. Container Class
A.K.A. Placeholder class
Dynamically creating fields:
class Container:
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
a = Container(firstname='Mark', lastname='Watney')
a.firstname # 'Mark'
a.lastname # 'Watney'
b = Container(species='Setosa')
b.species # 'Setosa'
Dynamically creating fields:
class Astronaut:
def __init__(self, lastname, **kwargs):
self.lastname = lastname
for key, value in kwargs.items():
setattr(self, key, value)
mark = Astronaut(lastname='Watney', addresses=())
melissa = Astronaut(firstname='Melissa', lastname='Lewis', agency='NASA')
print(mark.lastname) # Watney
print(melissa.firstname) # Melissa
print(mark.__dict__) # {'lastname': 'Watney', 'addresses': ()}
print(melissa.__dict__) # {'lastname': 'Melissa', 'firstname': 'Lewis', 'agency': 'NASA'}
class Container:
def __init__(self, **kwargs):
self.__dict__ = kwargs
a = Container(firstname='Mark', lastname='Watney')
print(a.firstname) # Mark
print(a.lastname) # Watney
b = Container(species='Setosa')
print(b.species) # Setosa
2.7.9. Example
DATA = [
{"firstname": "Mark", "lastname": "Watney", "addresses": [
{"street": "2101 E NASA Pkwy", "city": "Houston", "postcode": 77058, "region": "Texas", "country": "USA"},
{"street": "", "city": "Kennedy Space Center", "postcode": 32899, "region": "Florida", "country": "USA"}]},
{"firstname": "Melissa", "lastname": "Lewis", "addresses": [
{"street": "4800 Oak Grove Dr", "city": "Pasadena", "postcode": 91109, "region": "California", "country": "USA"},
{"street": "2825 E Ave P", "city": "Palmdale", "postcode": 93550, "region": "California", "country": "USA"}]},
{"firstname": "Rick", "lastname": "Martinez", "addresses": []},
{"firstname": "Alex", "lastname": "Vogel", "addresses": [
{"street": "Linder Hoehe", "city": "Cologne", "postcode": 51147, "region": "North Rhine-Westphalia", "country": "Germany"}]}
]
class Container:
def __init__(self, *args, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
def __repr__(self):
name = self.__class__.__name__
arguments = tuple(self.__dict__.values())
return f'\n\n{name}{arguments}'
result = [Container(**data)
for data in DATA]
print(result)
# [Container('Mark', 'Watney', [{'street': '2101 E NASA Pkwy', 'city': 'Houston', 'postcode': 77058, 'region': 'Texas', 'country': 'USA'}, {'street': '', 'city': 'Kennedy Space Center', 'postcode': 32899, 'region': 'Florida', 'country': 'USA'}]),
# Container('Melissa', 'Lewis', [{'street': '4800 Oak Grove Dr', 'city': 'Pasadena', 'postcode': 91109, 'region': 'California', 'country': 'USA'}, {'street': '2825 E Ave P', 'city': 'Palmdale', 'postcode': 93550, 'region': 'California', 'country': 'USA'}]),
# Container('Rick', 'Martinez', []),
# Container('Alex', 'Vogel', [{'street': 'Linder Hoehe', 'city': 'Cologne', 'postcode': 51147, 'region': 'North Rhine-Westphalia', 'country': 'Germany'}])]