Parameters
Short introduction to attrs
sequencing
relies on the attrs package for defining and instantiating classes with many parameters. attrs
allows you to define classes with minimal boilerplate by automating the creation of important methods like __init__
, __repr__
, __str__
, and comparison methods like __eq__
. For full details on attrs
, check out the documentation.
A class defined using attrs
must be decorated with @attr.s
(or attr.attrs
). In place of an explicit __init__
method, attributes of a class decorated with @attr.s
can have instance attributes defined at the class level using attr.ib()
(or attr.attrib()
). Adding attributes to a class using attr.ib()
has many advantages:
Attributes may be required or have a default value
Defaults can be defined either with a specific value or with a “factory” function that generates a default value when called
Attributes can have a
type
associated with them, or aconverter
function that converts the user-specified value into the desired formatAttributes can have
validators
which raise an error if an invalid value is provided
For the full list of options see attr.ib()
Parameterized
sequencing
builds on the functionality of attrs
using a class called Parameterized. Parameterized
objects must have a name
and can have any number of parameters
, which can be created using the functions defined in sequencing.parameters
, or by using attrs
directly via attr.ib().
Parameterized offers the following convenient features:
Recursive
get()
andset()
methods for getting and setting attributes of nestedParameterized
objects.Methods for converting a
Parameterized
object into a Pythondict
, and creating a newParameterized
object from adict
.Methods for serializing a
Parameterized
object tojson
and creating a newParameterized
object fromjson
.
Notes:
Subclasses of
Parameterized
must be decorated with@attr.s
Subclasses of
Parameterized
can define aninitialize()
method, which takes no arguments. It will be called on instantiation after theattrs
-generated__init__
method (see **attrs_post_init** for more details). If defined, the subclass’initialize()
method should always callsuper().initialize()
to ensure that the superclass is correctly initialized.
[1]:
import json
from pprint import pprint
import attr
from sequencing.parameters import (
Parameterized, StringParameter, BoolParameter, ListParameter, DictParameter,
IntParameter, FloatParameter, NanosecondParameter, GigahertzParameter, RadianParameter,
)
[2]:
@attr.s
class Engine(Parameterized):
cylinders = IntParameter(4)
displacement = FloatParameter(2, unit='liter')
current_rpm = FloatParameter(0, unit='rpm')
turbo_charged = BoolParameter(False)
@attr.s
class Transmission(Parameterized):
manual = BoolParameter(False)
num_gears = IntParameter(5)
current_gear = IntParameter(1)
def initialize(self):
super().initialize()
# Add private attributes in initialize()
self._is_broken = True
@property
def has_clutch(self):
return self.manual
def shift_to(self, gear):
if gear not in range(self.num_gears+1):
# 0 is reverse
raise ValueError(f'Cannot shift into gear {gear}')
if abs(gear - self.current_gear) > 1:
raise ValueError('Cannot skip gears')
self.current_gear = gear
@attr.s
class Car(Parameterized):
VALID_CHASSIS = ['sedan', 'coupe', 'hatchback', 'suv']
chassis = StringParameter('sedan', validator=attr.validators.in_(VALID_CHASSIS))
num_doors = IntParameter(4, validator=attr.validators.in_([2,4]))
miles_per_gallon = FloatParameter(30, unit='mpg')
engine = attr.ib(factory=lambda: Engine('engine'))
transmission = attr.ib(factory=lambda: Transmission('transmission'))
[3]:
car = Car('car') # All parameters other than name are optional because they have defaults
print(car)
Car(name='car', cls='__main__.Car', chassis='sedan', num_doors=4, miles_per_gallon=30.0, engine=Engine(name='engine', cls='__main__.Engine', cylinders=4, displacement=2.0, current_rpm=0.0, turbo_charged=False), transmission=Transmission(name='transmission', cls='__main__.Transmission', manual=False, num_gears=5, current_gear=1))
[4]:
pprint(car.as_dict())
{'chassis': 'sedan',
'cls': '__main__.Car',
'engine': {'cls': '__main__.Engine',
'current_rpm': 0.0,
'cylinders': 4,
'displacement': 2.0,
'name': 'engine',
'turbo_charged': False},
'miles_per_gallon': 30.0,
'name': 'car',
'num_doors': 4,
'transmission': {'cls': '__main__.Transmission',
'current_gear': 1,
'manual': False,
'name': 'transmission',
'num_gears': 5}}
[5]:
car2 = Car.from_dict(car.as_dict())
print(car == car2)
True
[6]:
car.get('engine.displacement') == {'engine.displacement': car.engine.displacement}
[6]:
True
[7]:
car.set_param('engine.displacement', 2.5)
car.get_param('engine.displacement') == car.engine.displacement == 2.5
[7]:
True
[8]:
car.set(engine__displacement=3.0)
print(car.get('engine.displacement'))
{'engine.displacement': 3.0}
[9]:
print(f'RPM: {car.engine.current_rpm}, gear: {car.transmission.current_gear}')
with car.temporarily_set(engine__current_rpm=4000, transmission__current_gear=3):
print(f'RPM: {car.engine.current_rpm}, gear: {car.transmission.current_gear}')
print(f'RPM: {car.engine.current_rpm}, gear: {car.transmission.current_gear}')
RPM: 0.0, gear: 1
RPM: 4000, gear: 3
RPM: 0.0, gear: 1
[10]:
try:
convertible = Car('convertible', chassis='convertible')
except ValueError as e:
print('ValueError:', e)
ValueError: ("'chassis' must be in ['sedan', 'coupe', 'hatchback', 'suv'] (got 'convertible')", Attribute(name='chassis', default='sedan', validator=<in_ validator with options ['sedan', 'coupe', 'hatchback', 'suv']>, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=<class 'str'>, kw_only=False, inherited=False, on_setattr=None), ['sedan', 'coupe', 'hatchback', 'suv'], 'convertible')
[11]:
try:
three_door = Car('three_door', num_doors=3)
except ValueError as e:
print('ValueError:', e)
ValueError: ("'num_doors' must be in [2, 4] (got 3)", Attribute(name='num_doors', default=4, validator=<in_ validator with options [2, 4]>, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=<class 'int'>, kw_only=False, inherited=False, on_setattr=None), [2, 4], 3)
[12]:
from qutip.ipynbtools import version_table
version_table()
[12]:
Software | Version |
---|---|
QuTiP | 4.7.0 |
Numpy | 1.23.2 |
SciPy | 1.9.1 |
matplotlib | 3.5.3 |
Cython | 0.29.32 |
Number of CPUs | 2 |
BLAS Info | OPENBLAS |
IPython | 8.4.0 |
Python | 3.8.6 (default, Oct 19 2020, 15:10:29) [GCC 7.5.0] |
OS | posix [linux] |
Tue Aug 30 19:26:45 2022 UTC |
[ ]: