.. _examples: Examples ======== 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. 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 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 `_. 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. A validator is simply a callable that takes two arguments: the attribute that it's validating .. 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 (got '42' that is a ).", Attribute(name='x', default_value=NOTHING, default_factory=NOTHING, validator=>), , '42') ``attrs`` ships with a bunch of validators, make sure to :ref:`check them out ` before writing your own!