2.8. Dynamic Typing — Python
2.8.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.8.2. Everything is an object
even function is an object!
2.8.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.8.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.8.5. Injecting properties
def add_numbers(a, b): """Function add numbers""" return a + b add_numbers.myattr = 10 print(add_numbers.myattr) # 10
2.8.6. Injecting methods
def add_numbers(a, b): """Function add numbers""" return a + b add_numbers.say_hello = lambda name: print(f'Hello {name}') add_numbers.say_hello('Alice') # Hello Alice
2.8.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.8.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.8.9. Example
DATA = [ {"firstname": "Alice", "lastname": "Apricot", "addresses": [ {"street": "2101 E NASA Pkwy", "city": "Houston", "postcode": "77058", "region": "Texas", "country": "USA"} ]}, {"firstname": "Bob", "lastname": "Blackthorn", "addresses": [ {"street": "", "city": "Kennedy Space Center", "postcode": "32899", "region": "Florida", "country": "USA"} ]}, {"firstname": "Carol", "lastname": "Corn", "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": "Dave", "lastname": "Durian", "addresses": [ {"street": "Linder Hoehe", "city": "Cologne", "postcode": "51147", "region": "North Rhine-Westphalia", "country": "Germany"} ]}, {"firstname": "Eve", "lastname": "Elderberry", "addresses": [ {"street": "", "city": "Космодро́м Байкону́р", "postcode": "", "region": "Кызылординская область", "country": "Қазақстан"}, {"street": "", "city": "Звёздный городо́к", "postcode": "141160", "region": "Московская область", "country": "Россия"} ]}, {"firstname": "Mallory", "lastname": "Melon", "addresses": []} ] 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)