File: examples.rst

package info (click to toggle)
python-characteristic 14.3.0-8
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 248 kB
  • sloc: python: 952; makefile: 148; sh: 5
file content (141 lines) | stat: -rw-r--r-- 4,933 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
.. _examples:

Examples
========

:func:`@attributes <characteristic.attributes>` together with the definition of the attributes using class attributes enhances your class by:

- a nice ``__repr__``,
- comparison methods that compare instances as if they were tuples of their attributes,
- and an initializer that uses the keyword arguments to initialize the specified attributes before running the class' own initializer (you just write the validator if you need anything more than type checks!).


.. doctest::

   >>> from characteristic import Attribute, attributes
   >>> @attributes(["a", "b"])
   ... class C(object):
   ...     pass
   >>> obj1 = C(a=1, b="abc")
   >>> obj1
   <C(a=1, b='abc')>
   >>> obj2 = C(a=2, b="abc")
   >>> obj1 == obj2
   False
   >>> obj1 < obj2
   True
   >>> obj3 = C(a=1, b="bca")
   >>> obj3 > obj1
   True

To offer more power and possibilities, ``characteristic`` comes with a distinct class to define attributes: :class:`~characteristic.Attribute`.
It allows for things like default values for certain attributes, making them optional when ``characteristic``\ 's generated initializer is used:

.. doctest::

   >>> @attributes(["a", "b", Attribute("c", default_value=42)])
   ... class CWithDefaults(object):
   ...     pass
   >>> obj4 = CWithDefaults(a=1, b=2)
   >>> obj4.characteristic_attributes
   [<Attribute(name='a', 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=None)>, <Attribute(name='b', 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=None)>, <Attribute(name='c', exclude_from_cmp=False, exclude_from_init=False, exclude_from_repr=False, exclude_from_immutable=False, default_value=42, default_factory=None, instance_of=None, init_aliaser=<function strip_leading_underscores at ...>)>]
   >>> obj5 = CWithDefaults(a=1, b=2, c=42)
   >>> obj4 == obj5
   True

``characteristic`` also offers factories for default values of complex types:

.. doctest::

   >>> @attributes([Attribute("a", default_factory=list),
   ...              Attribute("b", default_factory=dict)])
   ... class CWithDefaultFactory(object):
   ...     pass
   >>> obj6 = CWithDefaultFactory()
   >>> obj6
   <CWithDefaultFactory(a=[], b={})>
   >>> obj7 = CWithDefaultFactory()
   >>> obj7
   <CWithDefaultFactory(a=[], b={})>
   >>> obj6 == obj7
   True
   >>> obj6.a is obj7.a
   False
   >>> obj6.b is obj7.b
   False

You can also exclude certain attributes from certain decorators:

.. doctest::

   >>> @attributes(["host", "user",
   ...              Attribute("password", exclude_from_repr=True),
   ...              Attribute("_connection", exclude_from_init=True)])
   ... class DB(object):
   ...     _connection = None
   ...     def connect(self):
   ...         self._connection = "not really a connection"
   >>> db = DB(host="localhost", user="dba", password="secret")
   >>> db.connect()
   >>> db
   <DB(host='localhost', user='dba', _connection='not really a connection')>

Immutable data structures are amazing!
Guess what ``characteristic`` supports?

.. doctest::

   >>> @attributes([Attribute("a")], apply_immutable=True)
   ... class ImmutableClass(object):
   ...     pass
   >>> ic = ImmutableClass(a=42)
   >>> ic.a
   42
   >>> ic.a = 43
   Traceback (most recent call last):
    ...
   AttributeError: Attribute 'a' of class 'ImmutableClass' is immutable.
   >>> @attributes([Attribute("a")], apply_immutable=True)
   ... class AnotherImmutableClass(object):
   ...     def __init__(self):
   ...         self.a *= 2
   >>> ic2 = AnotherImmutableClass(a=21)
   >>> ic2.a
   42
   >>> ic.a = 43
   Traceback (most recent call last):
    ...
   AttributeError: Attribute 'a' of class 'AnotherImmutableClass' is immutable.

You know what else is amazing?
Type checks!

.. doctest::

   >>> @attributes([Attribute("a", instance_of=int)])
   ... class TypeCheckedClass(object):
   ...     pass
   >>> TypeCheckedClass(a="totally not an int")
   Traceback (most recent call last):
    ...
   TypeError: Attribute 'a' must be an instance of 'int'.


And if you want your classes to have certain attributes private, ``characteristic`` will keep your keyword arguments clean if not told otherwise\ [*]_:

.. doctest::

   >>> @attributes([Attribute("_private")])
   ... class CWithPrivateAttribute(object):
   ...     pass
   >>> obj8 = CWithPrivateAttribute(private=42)
   >>> obj8._private
   42
   >>> @attributes([Attribute("_private", init_aliaser=None)])
   ... class CWithPrivateAttributeNoAliasing(object):
   ...     pass
   >>> obj9 = CWithPrivateAttributeNoAliasing(_private=42)
   >>> obj9._private
   42

.. [*] This works *only* for attributes defined using the :class:`~characteristic.Attribute` class.