5.9. OOP Type¶
5.9.1. Class Definition¶
>>> class MyClass:
... pass
>>> MyClass = type('MyClass', (), {})
5.9.2. Static Attributes¶
>>> class MyClass:
... myattr = 1
>>> MyClass = type('MyClass', (), {'myattr': 1})
5.9.3. Static Methods¶
>>> class MyClass:
... def mymethod(): # noqa
... pass
>>> def mymethod():
... pass
>>>
>>> MyClass = type('MyClass', (), {'mymethod': mymethod})
5.9.4. Dynamic Methods¶
>>> class MyClass:
... def mymethod(self):
... pass
>>> def mymethod(self):
... pass
>>>
>>> MyClass = type('MyClass', (), {'mymethod': mymethod})
5.9.5. Init Method¶
>>> class User:
... def __init__(self, firstname, lastname):
... self.firstname = firstname
... self.lastname = lastname
...
... def hello(self):
... print('hello')
...
>>> def __init__(self, firstname, lastname):
... self.firstname = firstname
... self.lastname = lastname
>>>
>>> def hello(self):
... print('hello')
>>>
>>> User = type('User', (), {
... 'hello': hello,
... '__init__': __init__,
... })
5.9.6. Class Inheritance¶
>>> class Parent:
... pass
>>>
>>>
>>> class MyClass(Parent):
... pass
>>> MyClass = type('MyClass', (Parent,), {})
5.9.7. Recap¶
>>> class Parent:
... pass
>>>
>>>
>>> class MyClass(Parent):
... myattr = 1
...
... def mymethod(self):
... pass
>>> MyClass = type('MyClass', (Parent,), {'myattr': 1, 'mymethod': mymethod})
5.9.8. What is a class?¶
>>> def hello_static():
... print('hello')
>>>
>>> def hello_dynamic(self):
... print('hello')
>>>
>>> def myinit(self):
... ...
>>>
>>> def mystr(self):
... ...
>>>
>>> User = type('User', (), {
... '__init__': myinit,
... '__str__': mystr,
... '__repr__': mystr,
... 'firstname': 'Mark',
... 'lastname': 'Watney',
... 'hello_static': hello_static,
... 'hello_dynamic': hello_dynamic,
... })
>>> hex(id(hello_static))
0x1064e3910
>>>
>>> hex(id(hello_dynamic))
0x106082830
>>>
>>> hex(id(myinit))
0x103fb4540
>>>
>>> hex(id(mystr))
0x1064e2e60
>>>
>>> hex(id('Mark'))
0x1062527f0
>>>
>>> hex(id('Watney'))
0x1063502b0
>>>
... User = {
... '__init__': 0x103fb4540,
... '__str__': 0x1064e2e60,
... '__repr__': 0x1064e2e60,
... 'firstname': 0x1062527f0,
... 'lastname': 0x1063502b0,
... 'hello_static': 0x1064e3910,
... 'hello_dynamic': 0x106082830,
... }
Mind, how similar this is to C language struct:
>>> class User:
... firstname: str
... lastname: str
>>>
>>>
>>> mark = User()
>>>
>>> mark.firstname = 'Mark'
>>> mark.lastname = 'Watney'
>>>
>>> print(mark.firstname)
Mark
>>> print(mark.lastname)
Watney
struct User {
char firstname[30];
char lastname[30];
};
mark = (struct User*) malloc(sizeof(struct User));
mark->firstname = "Mark";
mark->lastname = "Watney";
printf(mark->firstname);
printf(mark->lastname);
5.9.9. Dynamic Class Creation¶
>>> Admin()
Traceback (most recent call last):
NameError: name 'Admin' is not defined
>>>
>>>
>>> for classname in ['Guest', 'User', 'Admin']:
... globals()[classname] = type(classname, (), {})
>>>
>>>
>>> Admin
<class '__main__.Admin'>
>>> Admin()
<__main__.Admin object at 0x...>
5.9.10. Use Case - 0x01¶
Init
>>> User = type('User', (), {
... 'firstname': 'Mark',
... 'lastname': 'Watney',
... 'hello': lambda: print('hello')})
>>>
>>> User.hello()
hello
>>>
>>> vars(User)
mappingproxy({'firstname': 'Mark',
'lastname': 'Watney',
'hello': <function <lambda> at 0x...>,
'__module__': '__main__',
'__dict__': <attribute '__dict__' of 'User' objects>,
'__weakref__': <attribute '__weakref__' of 'User' objects>,
'__doc__': None})
5.9.11. Use Case - 0x02¶
Dynamic Classes 1
>>> DATA = [
... ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
... (5.8, 2.7, 5.1, 1.9, 'virginica'),
... (5.1, 3.5, 1.4, 0.2, 'setosa'),
... (5.7, 2.8, 4.1, 1.3, 'versicolor'),
... (6.3, 2.9, 5.6, 1.8, 'virginica'),
... (6.4, 3.2, 4.5, 1.5, 'versicolor'),
... (4.7, 3.2, 1.3, 0.2, 'setosa'),
... (7.0, 3.2, 4.7, 1.4, 'versicolor'),
... (7.6, 3.0, 6.6, 2.1, 'virginica'),
... ]
>>>
>>>
>>> class Iris:
... def __init__(self, **kwargs):
... self.__dict__ = kwargs
...
... def __repr__(self):
... clsname = self.__class__.__name__
... values = tuple(vars(self).values())
... return f'{clsname}{values}'
>>>
>>>
>>> header, *rows = DATA
>>> header = [x.lower().replace(' ', '_') for x in header]
>>>
>>> result = []
>>>
>>> for *features,species in rows:
... features = dict(zip(header, features))
... clsname = species.capitalize()
... if clsname not in globals():
... globals()[clsname] = type(clsname, (Iris,), {})
... cls = globals()[clsname]
... iris = cls(**features)
... result.append(iris)
>>>
>>> result
[Virginica(5.8, 2.7, 5.1, 1.9),
Setosa(5.1, 3.5, 1.4, 0.2),
Versicolor(5.7, 2.8, 4.1, 1.3),
Virginica(6.3, 2.9, 5.6, 1.8),
Versicolor(6.4, 3.2, 4.5, 1.5),
Setosa(4.7, 3.2, 1.3, 0.2),
Versicolor(7.0, 3.2, 4.7, 1.4),
Virginica(7.6, 3.0, 6.6, 2.1)]
>>>
>>> vars(result[0])
{'sepal_length': 5.8,
'sepal_width': 2.7,
'petal_length': 5.1,
'petal_width': 1.9}
5.9.12. Use Case - 0x03¶
Dynamic Classes 2
>>> from dataclasses import dataclass
>>> from itertools import zip_longest
>>>
>>>
>>> DATA = [
... ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
... (5.8, 2.7, 'virginica'),
... (5.1, 3.5, 1.4, 0.2, 'setosa'),
... (5.7, 'versicolor'),
... (6.3, 2.9, 5.6, 1.8, 'virginica'),
... (6.4, 3.2, 4.5, 1.5, 'versicolor'),
... (4.7, 3.2, 1.3, 'setosa'),
... (7.0, 3.2, 4.7, 1.4, 'versicolor'),
... (7.6, 3.0, 'virginica'),
... ]
>>>
>>>
>>> @dataclass(init=False)
... class Iris:
... def __init__(self, **kwargs):
... self.__dict__ = kwargs
>>>
>>>
>>> result = []
>>> header, *rows = DATA
>>> header = [x.lower().replace(' ', '_') for x in header]
>>>
>>> for *features,species in rows:
... features = dict(zip_longest(header, features, fillvalue=None))
... clsname = species.capitalize()
... if clsname not in globals():
... globals()[clsname] = type(clsname, (Iris,), {})
... cls = globals()[clsname]
... iris = cls(**features)
... result.append(iris)
>>>
>>> result
[Virginica(5.8, 2.7, None, None, None),
Setosa(5.1, 3.5, 1.4, 0.2, None),
Versicolor(5.7, None, None, None, None),
Virginica(6.3, 2.9, 5.6, 1.8, None),
Versicolor(6.4, 3.2, 4.5, 1.5, None),
Setosa(4.7, 3.2, 1.3, None, None),
Versicolor(7.0, 3.2, 4.7, 1.4, None),
Virginica(7.6, 3.0, None, None, None)]
>>>
>>> vars(result[0])
{'sepal_length': 5.8,
'sepal_width': 2.7,
'petal_length': None,
'petal_width': None,
'species': None}
5.9.13. Use Case - 0x03¶
Factory
>>> DATA = [
... ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
... (5.8, 2.7, 5.1, 1.9, 'virginica'),
... (5.1, 3.5, 1.4, 0.2, 'setosa'),
... (5.7, 2.8, 4.1, 1.3, 'versicolor'),
... (6.3, 2.9, 5.6, 1.8, 'virginica'),
... (6.4, 3.2, 4.5, 1.5, 'versicolor'),
... (4.7, 3.2, 1.3, 0.2, 'setosa'),
... ]
>>>
>>>
>>> class Iris:
... def __init__(self, sepal_length, sepal_width, petal_length, petal_width):
... self.sepal_length = sepal_length
... self.sepal_width = sepal_width
... self.petal_length = petal_length
... self.petal_width = petal_width
...
... def __repr__(self):
... name = self.__class__.__name__
... values = tuple(self.__dict__.values())
... return f'{name}{values}'
>>>
>>>
>>> class Setosa(Iris):
... pass
>>>
>>> class Virginica(Iris):
... pass
>>>
>>> class Versicolor(Iris):
... pass
>>>
>>>
>>> def factory(species: str):
... if species == 'setosa':
... return Setosa
... if species == 'virginica':
... return Virginica
... if species == 'versicolor':
... return Versicolor
>>>
>>>
>>> result = []
>>>
>>> for *features, species in DATA[1:]:
... iris = factory(species)
... i = iris(*features)
... result.append(i)
>>>
>>> result
[Virginica(5.8, 2.7, 5.1, 1.9),
Setosa(5.1, 3.5, 1.4, 0.2),
Versicolor(5.7, 2.8, 4.1, 1.3),
Virginica(6.3, 2.9, 5.6, 1.8),
Versicolor(6.4, 3.2, 4.5, 1.5),
Setosa(4.7, 3.2, 1.3, 0.2)]
5.9.14. Use Case - 0x04¶
Object factory
>>> from dataclasses import dataclass
>>>
>>>
>>> DATA = [
... ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
... (5.8, 2.7, 5.1, 1.9, 'virginica'),
... (5.1, 3.5, 1.4, 0.2, 'setosa'),
... (5.7, 2.8, 4.1, 1.3, 'versicolor'),
... (6.3, 2.9, 5.6, 1.8, 'virginica'),
... (6.4, 3.2, 4.5, 1.5, 'versicolor'),
... (4.7, 3.2, 1.3, 0.2, 'setosa'),
... ]
>>>
>>>
>>> @dataclass
... class Iris:
... sepal_length: float
... sepal_width: float
... petal_length: float
... petal_width: float
>>>
>>> class Setosa(Iris):
... pass
>>>
>>> class Virginica(Iris):
... pass
>>>
>>> class Versicolor(Iris):
... pass
>>>
>>>
>>> def factory(species: str):
... clsname = species.capitalize()
... classes = globals()
... return classes[clsname]
>>>
>>>
>>> result = [factory(species)(*features)
... for *features, species in DATA[1:]]
>>>
>>> result
[Virginica(sepal_length=5.8, sepal_width=2.7, petal_length=5.1, petal_width=1.9),
Setosa(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2),
Versicolor(sepal_length=5.7, sepal_width=2.8, petal_length=4.1, petal_width=1.3),
Virginica(sepal_length=6.3, sepal_width=2.9, petal_length=5.6, petal_width=1.8),
Versicolor(sepal_length=6.4, sepal_width=3.2, petal_length=4.5, petal_width=1.5),
Setosa(sepal_length=4.7, sepal_width=3.2, petal_length=1.3, petal_width=0.2)]
5.9.15. Use Case - 0x05¶
Class factory
>>> from dataclasses import dataclass
>>>
>>>
>>> DATA = [
... ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
... (5.8, 2.7, 5.1, 1.9, 'virginica'),
... (5.1, 3.5, 1.4, 0.2, 'setosa'),
... (5.7, 2.8, 4.1, 1.3, 'versicolor'),
... (6.3, 2.9, 5.6, 1.8, 'virginica'),
... (6.4, 3.2, 4.5, 1.5, 'versicolor'),
... (4.7, 3.2, 1.3, 0.2, 'setosa'),
... ]
>>>
>>>
>>> @dataclass
... class Iris:
... sepal_length: float
... sepal_width: float
... petal_length: float
... petal_width: float
>>>
>>>
>>>
>>> def factory(species: str):
... clsname = species.capitalize()
... classes = globals()
... if clsname not in classes:
... classes[clsname] = type(clsname, (Iris,), {})
... return classes[clsname]
>>>
>>>
>>> result = [factory(species)(*features)
... for *features, species in DATA[1:]]
>>>
>>> result
[Virginica(sepal_length=5.8, sepal_width=2.7, petal_length=5.1, petal_width=1.9),
Setosa(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2),
Versicolor(sepal_length=5.7, sepal_width=2.8, petal_length=4.1, petal_width=1.3),
Virginica(sepal_length=6.3, sepal_width=2.9, petal_length=5.6, petal_width=1.8),
Versicolor(sepal_length=6.4, sepal_width=3.2, petal_length=4.5, petal_width=1.5),
Setosa(sepal_length=4.7, sepal_width=3.2, petal_length=1.3, petal_width=0.2)]
5.9.16. Use Case - 0x06¶
>>> from dataclasses import dataclass
>>>
>>>
>>> DATA = [
... ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
... (5.8, 2.7, 5.1, 1.9, 'virginica'),
... (5.1, 3.5, 1.4, 0.2, 'setosa'),
... (5.7, 2.8, 4.1, 1.3, 'versicolor'),
... (6.3, 2.9, 5.6, 1.8, 'virginica'),
... (6.4, 3.2, 4.5, 1.5, 'versicolor'),
... (4.7, 3.2, 1.3, 0.2, 'setosa'),
... ]
>>>
>>>
>>> @dataclass
... class Iris:
... sepal_length: float
... sepal_width: float
... petal_length: float
... petal_width: float
>>>
>>>
>>> def factory(row):
... *features, species = row
... clsname = species.capitalize()
... cls = type(clsname, (Iris,), {})
... return cls(*features)
>>>
>>>
>>> result = map(factory, DATA[1:])
>>>
>>> list(result)
[Virginica(sepal_length=5.8, sepal_width=2.7, petal_length=5.1, petal_width=1.9),
Setosa(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2),
Versicolor(sepal_length=5.7, sepal_width=2.8, petal_length=4.1, petal_width=1.3),
Virginica(sepal_length=6.3, sepal_width=2.9, petal_length=5.6, petal_width=1.8),
Versicolor(sepal_length=6.4, sepal_width=3.2, petal_length=4.5, petal_width=1.5),
Setosa(sepal_length=4.7, sepal_width=3.2, petal_length=1.3, petal_width=0.2)]
5.9.17. Use Case - 0x07¶
>>> from pprint import pprint
>>>
>>>
>>> DATA = [
... ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
... (5.8, 2.7, 5.1, 1.9, 'virginica'),
... (5.1, 3.5, 1.4, 0.2, 'setosa'),
... (5.7, 2.8, 4.1, 1.3, 'versicolor'),
... (6.3, 2.9, 5.6, 1.8, 'virginica'),
... (6.4, 3.2, 4.5, 1.5, 'versicolor'),
... (4.7, 3.2, 1.3, 0.2, 'setosa'),
... (7.0, 3.2, 4.7, 1.4, 'versicolor'),
... (7.6, 3.0, 6.6, 2.1, 'virginica'),
... ]
>>>
>>>
>>> def myinit(self, sl, sw, pl, pw):
... self.sl = sl
... self.sw = sw
... self.pl = pl
... self.pw = pw
>>>
>>> def myrepr(self):
... clsname = self.__class__.__name__
... values = tuple(vars(self).values())
... return f'{clsname}{values}'
>>>
>>> iris = type('Iris', (), {'__init__': myinit, '__repr__': myrepr})
>>>
>>> result = [cls(*values)
... for *values, species in DATA[1:]
... if (clsname := species.capitalize())
... and (cls := type(clsname, (iris,), {}))]
>>>
>>>
>>> pprint(result)
[Virginica(5.8, 2.7, 5.1, 1.9),
Setosa(5.1, 3.5, 1.4, 0.2),
Versicolor(5.7, 2.8, 4.1, 1.3),
Virginica(6.3, 2.9, 5.6, 1.8),
Versicolor(6.4, 3.2, 4.5, 1.5),
Setosa(4.7, 3.2, 1.3, 0.2),
Versicolor(7.0, 3.2, 4.7, 1.4),
Virginica(7.6, 3.0, 6.6, 2.1)]
5.9.18. Use Case - 0x08¶
>>> DATA = [
... ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
... (5.8, 2.7, 5.1, 1.9, 'virginica'),
... (5.1, 3.5, 1.4, 0.2, 'setosa'),
... (5.7, 2.8, 4.1, 1.3, 'versicolor'),
... (6.3, 2.9, 5.6, 1.8, 'virginica'),
... (6.4, 3.2, 4.5, 1.5, 'versicolor'),
... (4.7, 3.2, 1.3, 0.2, 'setosa'),
... ]
>>> def myinit(self, **kwargs):
... self.__dict__ = kwargs
>>>
>>> def mystr(self):
... clsname = self.__class__.__name__
... values = tuple(vars(self).values())
... return f'{clsname}{values}'
>>>
>>> Iris = type('Iris', (), {
... '__init__': myinit,
... '__str__': mystr,
... '__repr__': mystr})
>>> result = []
>>> header = [x.lower().replace(' ', '_') for x in DATA[0]]
>>> data = [dict(zip(header,row)) for row in DATA[1:]]
>>>
>>> for row in data:
... clsname = row.pop('species').capitalize()
... if clsname not in globals():
... globals()[clsname] = type(clsname, (Iris,), {})
... cls = globals()[clsname]
... iris = cls(**row)
... result.append(iris)
>>> pprint(result, width=120)
[Virginica(sepal_length=5.8, sepal_width=2.7, petal_length=5.1, petal_width=1.9),
Setosa(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2),
Versicolor(sepal_length=5.7, sepal_width=2.8, petal_length=4.1, petal_width=1.3),
Virginica(sepal_length=6.3, sepal_width=2.9, petal_length=5.6, petal_width=1.8),
Versicolor(sepal_length=6.4, sepal_width=3.2, petal_length=4.5, petal_width=1.5),
Setosa(sepal_length=4.7, sepal_width=3.2, petal_length=1.3, petal_width=0.2)]
5.9.19. Use Case - 0x09¶
>>> from itertools import zip_longest
>>>
>>>
>>> DATA = [
... ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
... (5.8, 2.7, 'virginica'),
... (5.1, 3.5, 1.4, 0.2, 'setosa'),
... (5.7, 'versicolor'),
... (6.3, 2.9, 5.6, 1.8, 'virginica'),
... (6.4, 3.2, 4.5, 1.5, 'versicolor'),
... (4.7, 3.2, 1.3, 'setosa'),
... (7.0, 3.2, 4.7, 1.4, 'versicolor'),
... (7.6, 3.0, 'virginica'),
... ]
>>>
>>>
>>> Iris = type('Iris', (), {
... '__init__': lambda self, **kwargs: self.__dict__.update(kwargs),
... '__repr__': lambda self: f'{self.__class__.__name__}{tuple(vars(self).values())}'})
>>>
>>> header = [x.lower().replace(' ', '_') for x in DATA[0]]
>>>
>>> result = [cls(**values)
... for *features,species in DATA[1:]
... if (values := dict(zip_longest(header, features, fillvalue=None)))
... and (clsname := species.capitalize())
... and (cls := type(clsname, (Iris,), {}))]
>>> result
[Virginica(5.8, 2.7, None, None, None),
Setosa(5.1, 3.5, 1.4, 0.2, None),
Versicolor(5.7, None, None, None, None),
Virginica(6.3, 2.9, 5.6, 1.8, None),
Versicolor(6.4, 3.2, 4.5, 1.5, None),
Setosa(4.7, 3.2, 1.3, None, None),
Versicolor(7.0, 3.2, 4.7, 1.4, None),
Virginica(7.6, 3.0, None, None, None)]
>>> result[0]
Virginica(5.8, 2.7, None, None, None)
>>> vars(result[0])
{'sepal_length': 5.8,
'sepal_width': 2.7,
'petal_length': None,
'petal_width': None,
'species': None}
5.9.20. Use Case - 0x10¶
SetUp:
>>> del Iris
>>> del Setosa
>>> del Virginica
>>> del Versicolor
>>> del cls
>>> del values
>>> del species
>>> del iris
>>> del result
Code:
>>> DATA = [
... ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
... (5.8, 2.7, 5.1, 1.9, 'virginica'),
... (5.1, 3.5, 1.4, 0.2, 'setosa'),
... (5.7, 2.8, 4.1, 1.3, 'versicolor'),
... (6.3, 2.9, 5.6, 1.8, 'virginica'),
... (6.4, 3.2, 4.5, 1.5, 'versicolor'),
... (4.7, 3.2, 1.3, 0.2, 'setosa'),
... (7.0, 3.2, 4.7, 1.4, 'versicolor'),
... (7.6, 3.0, 6.6, 2.1, 'virginica'),
... (4.9, 3.0, 1.4, 0.2, 'setosa'),
... (4.9, 2.5, 4.5, 1.7, 'virginica'),
... (7.1, 3.0, 5.9, 2.1, 'virginica'),
... (4.6, 3.4, 1.4, 0.3, 'setosa'),
... (5.4, 3.9, 1.7, 0.4, 'setosa'),
... (5.7, 2.8, 4.5, 1.3, 'versicolor'),
... (5.7, 2.8, 4.5, 1.3, 'setosa'),
... (5.0, 3.6, 1.4, 0.3, 'setosa'),
... (5.5, 2.3, 4.0, 1.3, 'versicolor'),
... (6.5, 3.0, 5.8, 2.2, 'virginica'),
... (6.5, 2.8, 4.6, 1.5, 'versicolor'),
... (6.3, 3.3, 6.0, 2.5, 'virginica'),
... (6.9, 3.1, 4.9, 1.5, 'versicolor'),
... (4.6, 3.1, 1.5, 0.2, 'setosa'),
... ]
>>>
>>>
>>> globals()['Iris'] = type('Iris', (), {
... '__init__': lambda self, *args: setattr(self, 'features', tuple(args)),
... '__repr__': lambda self: f'{self.__class__.__name__}{self.features}',
... })
>>>
>>>
>>> def iris(row):
... *values, species = row
... clsname = species.capitalize()
... if clsname not in globals():
... cls = type(clsname, (globals()['Iris'],), {})
... return cls(*values)
>>>
>>>
>>> result = map(iris, DATA[1:])
>>>
>>> list(result)
[Virginica(5.8, 2.7, 5.1, 1.9),
Setosa(5.1, 3.5, 1.4, 0.2),
Versicolor(5.7, 2.8, 4.1, 1.3),
Virginica(6.3, 2.9, 5.6, 1.8),
Versicolor(6.4, 3.2, 4.5, 1.5),
Setosa(4.7, 3.2, 1.3, 0.2),
Versicolor(7.0, 3.2, 4.7, 1.4),
Virginica(7.6, 3.0, 6.6, 2.1),
Setosa(4.9, 3.0, 1.4, 0.2),
Virginica(4.9, 2.5, 4.5, 1.7),
Virginica(7.1, 3.0, 5.9, 2.1),
Setosa(4.6, 3.4, 1.4, 0.3),
Setosa(5.4, 3.9, 1.7, 0.4),
Versicolor(5.7, 2.8, 4.5, 1.3),
Setosa(5.7, 2.8, 4.5, 1.3),
Setosa(5.0, 3.6, 1.4, 0.3),
Versicolor(5.5, 2.3, 4.0, 1.3),
Virginica(6.5, 3.0, 5.8, 2.2),
Versicolor(6.5, 2.8, 4.6, 1.5),
Virginica(6.3, 3.3, 6.0, 2.5),
Versicolor(6.9, 3.1, 4.9, 1.5),
Setosa(4.6, 3.1, 1.5, 0.2)]
5.9.21. Use Case - 0x11¶
>>> DATA = [
... ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
... (5.8, 2.7, 5.1, 1.9, 'virginica'),
... (5.1, 3.5, 1.4, 0.2, 'setosa'),
... (5.7, 2.8, 4.1, 1.3, 'versicolor'),
... (6.3, 2.9, 5.6, 1.8, 'virginica'),
... (6.4, 3.2, 4.5, 1.5, 'versicolor'),
... (4.7, 3.2, 1.3, 0.2, 'arctica'),
... (7.0, 3.2, 4.7, 1.4, 'versicolor'),
... (7.6, 3.0, 6.6, 2.1, 'virginica'),
... ]
...
>>> header, *rows = DATA
...
>>> globals()['Iris'] = type('Iris', (), {
... '__init__': lambda self, **kwargs: self.__dict__.update(kwargs),
... '__str__': lambda self: f'{self.__class__.__name__}{tuple(vars(self).values())}',
... })
...
>>> def factory(row):
... *values, species = row
... clsname = species.capitalize()
... if clsname not in globals():
... globals()[clsname] = type(clsname, (globals()['Iris'],), {})
... cls = globals()[clsname]
... kwargs = dict(zip(header, values))
... return cls(**kwargs)
...
>>> for iris in map(factory, rows):
... print(iris)
...
Virginica(5.8, 2.7, 5.1, 1.9)
Setosa(5.1, 3.5, 1.4, 0.2)
Versicolor(5.7, 2.8, 4.1, 1.3)
Virginica(6.3, 2.9, 5.6, 1.8)
Versicolor(6.4, 3.2, 4.5, 1.5)
Arctica(4.7, 3.2, 1.3, 0.2)
Versicolor(7.0, 3.2, 4.7, 1.4)
Virginica(7.6, 3.0, 6.6, 2.1)
5.9.22. Assignments¶
"""
* Assignment: OOP ClassFactory Iris
* Complexity: medium
* Lines of code: 8 lines
* Time: 8 min
English:
1. Create `result: list[Iris]`
2. Iterate over `DATA` skipping header
3. Separate `features` from `species` in each row
4. Append to `result`:
a. if `species` is "setosa" append instance of a class `Setosa`
b. if `species` is "versicolor" append instance of a class `Versicolor`
c. if `species` is "virginica" append instance of a class `Virginica`
5. Initialize instances with `features` using `*args` notation
6. Run doctests - all must succeed
Polish:
1. Stwórz `result: list[Iris]`
2. Iterując po `DATA` pomijając header
3. Odseparuj `features` od `species` w każdym wierszu
4. Dodaj do `result`:
a. jeżeli `species` jest "setosa" to dodaj instancję klasy `Setosa`
b. jeżeli `species` jest "versicolor" to dodaj instancję klasy `Versicolor`
c. jeżeli `species` jest "virginica" to dodaj instancję klasy `Virginica`
5. Instancje inicjalizuj danymi z `features` używając notacji `*args`
6. Uruchom doctesty - wszystkie muszą się powieść
Hints:
* `globals()[classname]`
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> list(result) # doctest: +NORMALIZE_WHITESPACE
[Virginica(5.8, 2.7, 5.1, 1.9),
Setosa(5.1, 3.5, 1.4, 0.2),
Versicolor(5.7, 2.8, 4.1, 1.3),
Virginica(6.3, 2.9, 5.6, 1.8),
Versicolor(6.4, 3.2, 4.5, 1.5),
Setosa(4.7, 3.2, 1.3, 0.2)]
"""
from dataclasses import dataclass
DATA = [
('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
(5.8, 2.7, 5.1, 1.9, 'virginica'),
(5.1, 3.5, 1.4, 0.2, 'setosa'),
(5.7, 2.8, 4.1, 1.3, 'versicolor'),
(6.3, 2.9, 5.6, 1.8, 'virginica'),
(6.4, 3.2, 4.5, 1.5, 'versicolor'),
(4.7, 3.2, 1.3, 0.2, 'setosa'),
]
@dataclass(repr=False)
class Iris:
_sepal_length: float
_sepal_width: float
_petal_length: float
_petal_width: float
def __repr__(self):
name = self.__class__.__name__
args = tuple(self.__dict__.values())
return f'{name}{args}'
# Append to `result`:
# Use type() to create classes dynamically
# a. if `species` is "setosa" append instance of a class `Setosa`
# b. if `species` is "versicolor" append instance of a class `Versicolor`
# c. if `species` is "virginica" append instance of a class `Virginica`
# type: list[Iris]
result = ...