2015-01-27 16:53:17 +00:00
.. _examples:
Examples
========
2015-01-28 15:28:47 +00:00
The simplest possible usage would be:
.. doctest ::
>>> import attr
>>> @attr.s
... class Empty(object):
... pass
>>> Empty()
Empty()
>>> Empty() == Empty()
True
>>> Empty() is Empty()
False
So in other words: `` attrs `` useful even without actual attributes!
But you'll usually want some data on your classes, so let's add some:
.. doctest ::
>>> @attr.s
... class Coordinates(object):
... x = attr.ib()
... y = attr.ib()
These by default, all features are added, so you have immediately a fully functional data class with a nice `` repr `` string and comparison methods.
.. doctest ::
>>> c1 = Coordinates(1, 2)
>>> c1
Coordinates(x=1, y=2)
>>> c2 = Coordinates(x=2, y=1)
>>> c2
Coordinates(x=2, y=1)
>>> c1 == c2
False
As shown, the generated `` __init__ `` method allows both for positional and keyword arguments.
2015-01-28 16:36:58 +00:00
2015-01-29 09:17:08 +00:00
If playful naming turns you off, `` attrs `` comes with no-nonsense aliases:
.. doctest ::
>>> @attr.attributes
... class SeriousCoordinates(object):
... x = attr.attr()
... y = attr.attr()
>>> SeriousCoordinates(1, 2)
SeriousCoordinates(x=1, y=2)
>>> attr.ls(Coordinates) == attr.ls(SeriousCoordinates)
True
2015-01-28 16:36:58 +00:00
Sometimes you want to have default values for your initializer.
And sometimes you even want mutable objects as default values (ever used accidentally `` def f(arg=[]) `` ?).
`` attrs `` has you covered in both cases:
.. doctest ::
>>> import collections
>>> @attr.s
... class Connection(object):
... socket = attr.ib()
... @classmethod
... def connect(cl, db_string):
... # connect somehow to db_string
... return cl(socket=42)
>>> @attr.s
... class ConnectionPool(object):
... db_string = attr.ib()
... pool = attr.ib(default_factory=collections.deque)
... debug = attr.ib(default_value=False)
... def get_connection(self):
... try:
... return self.pool.pop()
... except IndexError:
... if self.debug:
... print "New connection!"
... return Connection.connect(self.db_string)
... def free_connection(self, conn):
... if self.debug:
... print "Connection returned!"
... self.pool.appendleft(conn)
...
>>> cp = ConnectionPool("postgres://localhost")
>>> cp
ConnectionPool(db_string='postgres://localhost', pool=deque([]), debug=False)
>>> conn = cp.get_connection()
>>> conn
Connection(socket=42)
>>> cp.free_connection(conn)
>>> cp
ConnectionPool(db_string='postgres://localhost', pool=deque([Connection(socket=42)]), debug=False)
More information on why class methods for constructing objects are awesome can be found in this insightful `blog post <http://as.ynchrono.us/2014/12/asynchronous-object-initialization.html> `_ .
2015-01-29 11:20:17 +00:00
Although your initializers should be a dumb as possible, it can come handy to do some kind of validation on the arguments.
That's when :func: `attr.ib` \ ’ s `` validator `` argument comes into play.
2015-01-29 11:26:25 +00:00
A validator is simply a callable that takes two arguments: the attribute that it's validating and the value that is passed for it.
If the value does not pass the validator's standards, it just raises an appropriate exception:
.. doctest ::
>>> def smaller_than_5(attribute, value):
... if value >= 5:
... raise ValueError("'{name}' has to be smaller than 5!"
... .format(name=attribute.name))
>>> @attr.s
... class C(object):
... x = attr.ib(validator=smaller_than_5)
>>> C(42)
Traceback (most recent call last):
...
ValueError: 'x' has to be smaller than 5!
`` attrs `` ships with a bunch of validators, make sure to :ref: `check them out <api_validators>` before writing your own:
2015-01-29 11:20:17 +00:00
.. doctest ::
>>> @attr.s
... class C(object):
... x = attr.ib(validator=attr.validators.instance_of(int))
>>> C(42)
C(x=42)
>>> C("42")
Traceback (most recent call last):
...
TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default_value=NOTHING, default_factory=NOTHING, validator=<instance_of validator for type <type 'int'>>), <type 'int'>, '42')
2015-01-29 12:05:04 +00:00
For private attributes, `` attrs `` will strip the leading underscores for keyword arguments:
.. doctest ::
>>> @attr.s
... class C(object):
... _x = attr.ib()
>>> C(x=1)
C(_x=1)