API

characteristic consists of several class decorators that add features to your classes. There are four that add one feature each to your class. And then there’s the helper @attributes that combines them all into one decorator so you don’t have to repeat the attribute list multiple times.

Generally the decorators take a list of attributes as their first positional argument. This list can consists of either native strings[*] for simple cases or instances of Attribute that allow for more customization of characteristic‘s behavior.

The easiest way to get started is to have a look at the Examples to get a feeling for characteristic and return later for details!

[*]Byte strings on Python 2 and Unicode strings on Python 3.

Note

Every argument except for attrs for decorators and name for Attribute is a keyword argument. Their positions are coincidental and not guaranteed to remain stable.

characteristic.attributes(attrs, apply_with_cmp=True, apply_with_init=True, apply_with_repr=True, apply_immutable=False, store_attributes=<function _default_store_attributes>, **kw)

A convenience class decorator that allows to selectively apply with_cmp(), with_repr(), with_init(), and immutable() to avoid code duplication.

Parameters:
  • attrs (list of str or Attributes.) – Attributes to work with.
  • apply_with_cmp (bool) – Apply with_cmp().
  • apply_with_init (bool) – Apply with_init().
  • apply_with_repr (bool) – Apply with_repr().
  • apply_immutable (bool) – Apply immutable(). The only one that is off by default.
  • store_attributes (callable) – Store the given attrs on the class. Should accept two arguments, the class and the attributes, in that order. Note that attributes passed in will always be instances of Attribute, (so simple string attributes will already have been converted). By default if unprovided, attributes are stored in a characteristic_attributes attribute on the class.
Raises:

ValueError – If both defaults and an instance of Attribute has been passed.

New in version 14.0: Added possibility to pass instances of Attribute in attrs.

New in version 14.0: Added apply_*.

New in version 14.2: Added store_attributes.

Deprecated since version 14.0: Use Attribute instead of defaults.

Parameters:defaults (dict or None) – Default values if attributes are omitted on instantiation.

Deprecated since version 14.0: Use apply_with_init instead of create_init. Until removal, if either if False, with_init is not applied.

Parameters:create_init (bool) – Apply with_init().
characteristic.with_repr(attrs)

A class decorator that adds a human readable __repr__ method to your class using attrs.

Parameters:attrs (list of str or Attributes.) – Attributes to work with.
>>> from characteristic import with_repr
>>> @with_repr(["a", "b"])
... class RClass(object):
...     def __init__(self, a, b):
...         self.a = a
...         self.b = b
>>> c = RClass(42, "abc")
>>> print c
<RClass(a=42, b='abc')>
characteristic.with_cmp(attrs)

A class decorator that adds comparison methods and a hashing method based on attrs.

For that, each class is treated like a tuple of the values of attrs, so objectA == objectB -> True and objectA.__hash__() == objectB.__hash() -> True iff objectA’s tuple of attrs == objectB’s tuple of attrs. But only instances of identical classes are compared!

Parameters:attrs (list of str or Attributes.) – Attributes to work with.
>>> from characteristic import with_cmp
>>> @with_cmp(["a", "b"])
... class CClass(object):
...     def __init__(self, a, b):
...         self.a = a
...         self.b = b
>>> o1 = CClass(1, "abc")
>>> o2 = CClass(1, "abc")
>>> o1 == o2  # o1.a == o2.a and o1.b == o2.b
True
>>> o1.c = 23
>>> o2.c = 42
>>> o1 == o2  # attributes that are not passed to with_cmp are ignored
True
>>> o3 = CClass(2, "abc")
>>> o1 < o3  # because 1 < 2
True
>>> o4 = CClass(1, "bca")
>>> o1 < o4  # o1.a == o4.a, but o1.b < o4.b
True
characteristic.with_init(attrs, **kw)

A class decorator that wraps the __init__ method of a class and sets attrs using passed keyword arguments before calling the original __init__.

Those keyword arguments that are used, are removed from the kwargs that is passed into your original __init__. Optionally, a dictionary of default values for some of attrs can be passed too.

Attributes that are defined using Attribute and start with underscores will get them stripped for the initializer arguments by default (this behavior is changeable on per-attribute basis when instantiating Attribute.

Parameters:

attrs (list of str or Attributes.) – Attributes to work with.

Raises:
  • ValueError – If the value for a non-optional attribute hasn’t been passed as a keyword argument.
  • ValueError – If both defaults and an instance of Attribute has been passed.

Deprecated since version 14.0: Use Attribute instead of defaults.

Parameters:defaults (dict or None) – Default values if attributes are omitted on instantiation.
>>> from characteristic import with_init, Attribute
>>> @with_init(["a",
...             Attribute("b", default_factory=lambda: 2),
...             Attribute("_c")])
... class IClass(object):
...     def __init__(self):
...         if self.b != 2:
...             raise ValueError("'b' must be 2!")
>>> o1 = IClass(a=1, b=2, c=3)
>>> o2 = IClass(a=1, c=3)
>>> o1._c
3
>>> o1.a == o2.a
True
>>> o1.b == o2.b
True
>>> IClass()
Traceback (most recent call last):
  ...
ValueError: Missing keyword value for 'a'.
>>> IClass(a=1, b=3)  # the custom __init__ is called after the attributes are initialized
Traceback (most recent call last):
  ...
ValueError: 'b' must be 2!

Note

The generated initializer explicitly does not support positional arguments. Those are always passed to the existing __init__ unaltered. Used keyword arguments will not be passed to the original __init__ method and have to be accessed on the class (i.e. self.a).

characteristic.immutable(attrs)

Class decorator that makes attrs of a class immutable.

That means that attrs can only be set from an initializer. If anyone else tries to set one of them, an AttributeError is raised.

New in version 14.0.

>>> from characteristic import immutable
>>> @immutable([Attribute("foo")])
... class ImmutableClass(object):
...     foo = "bar"
>>> ic = ImmutableClass()
>>> ic.foo
'bar'
>>> ic.foo = "not bar"
Traceback (most recent call last):
  ...
AttributeError: Attribute 'foo' of class 'ImmutableClass' is immutable.

Please note, that that doesn’t mean that the attributes themselves are immutable too:

>>> @immutable(["foo"])
... class C(object):
...     foo = []
>>> i = C()
>>> i.foo = [42]
Traceback (most recent call last):
 ...
AttributeError: Attribute 'foo' of class 'C' is immutable.
>>> i.foo.append(42)
>>> i.foo
[42]
class characteristic.Attribute(name, exclude_from_cmp=False, exclude_from_init=False, exclude_from_repr=False, exclude_from_immutable=False, default_value=NOTHING, default_factory=None, instance_of=None, init_aliaser=<function strip_leading_underscores>)

A representation of an attribute.

In the simplest case, it only consists of a name but more advanced properties like default values are possible too.

All attributes on the Attribute class are read-only.

Parameters:
  • name (str) – Name of the attribute.
  • exclude_from_cmp (bool) – Ignore attribute in with_cmp().
  • exclude_from_init (bool) – Ignore attribute in with_init().
  • exclude_from_repr (bool) – Ignore attribute in with_repr().
  • exclude_from_immutable (bool) – Ignore attribute in immutable().
  • default_value

    A value that is used whenever this attribute isn’t passed as an keyword argument to a class that is decorated using with_init() (or attributes() with apply_with_init=True).

    Therefore, setting this makes an attribute optional.

    Since a default value of None would be ambiguous, a special sentinel NOTHING is used. Passing it means the lack of a default value.

  • default_factory (callable) –

    A factory that is used for generating default values whenever this attribute isn’t passed as an keyword argument to a class that is decorated using with_init() (or attributes() with apply_with_init=True).

    Therefore, setting this makes an attribute optional.

  • instance_of (type) – If used together with with_init() (or attributes() with apply_with_init=True), the passed value is checked whether it’s an instance of the type passed here. The initializer then raises TypeError on mismatch.
  • init_aliaser (callable) – A callable that is invoked with the name of the attribute and whose return value is used as the keyword argument name for the __init__ created by with_init() (or attributes() with apply_with_init=True). Uses strip_leading_underscores() by default to change _foo to foo. Set to None to disable aliasing.
Raises:

ValueError – If both default_value and default_factory have been passed.

New in version 14.0.

characteristic.strip_leading_underscores(attribute_name)

Strip leading underscores from attribute_name.

Used by default by the init_aliaser argument of Attribute.

Parameters:attribute_name (str) – The original attribute name to mangle.
Return type:str
>>> from characteristic import strip_leading_underscores
>>> strip_leading_underscores("_foo")
'foo'
>>> strip_leading_underscores("__bar")
'bar'
>>> strip_leading_underscores("___qux")
'qux'
characteristic.NOTHING = NOTHING

Sentinel to indicate the lack of a value when None is ambiguous.

New in version 14.0.