===== attrs ===== Attrs, allow you to define a set of attributes that will be applied later to a class or object. >>> from flask.ext.dry import attrs Basics ====== Attrs are created with keyword arguments: >>> a = attrs(foo=1, bar=2) They may be copied into a class: >>> class c: pass >>> a.copy_into(c) >>> c.foo 1 >>> c.bar 2 Or an object: >>> class d: ... # for lookup modifier, later ... def get_expanded_attr(self, attr): return getattr(self, attr) >>> obj = d() >>> a.copy_into(obj) >>> obj.foo 1 >>> obj.bar 2 In the latter case, the class isn't changed: >>> d.foo Traceback (most recent call last): ... AttributeError: type object 'd' has no attribute 'foo' They may also be changed, like any other object: >>> a.foo = 100 >>> obj2 = d() >>> a.copy_into(obj2) >>> obj2.foo 100 >>> obj2.bar 2 Finally, attrs make a deepcopy_ of all values assigned to them, as well as the values copied into the receiving class or object so that one object can not corrupt the value of another. >>> l = [1, 2, 3] >>> a = attrs(foo=l) # foo is a deepcopy of l >>> obj4 = d() >>> a.copy_into(obj4) # obj4.foo is a deepcopy of a.foo >>> a.foo.append(4) >>> obj5 = d() >>> a.copy_into(obj5) # obj5.foo is a second deepcopy of a.foo >>> obj4.foo.append(5) >>> obj5.foo.append(6) >>> obj4.foo [1, 2, 3, 5] >>> obj5.foo [1, 2, 3, 4, 6] >>> a.foo [1, 2, 3, 4] >>> l [1, 2, 3] Modifiers ========= Normally, the attrs attributes replace any attributes of the same name already in the class or object. >>> a = attrs(foo=1, bar=2) >>> obj6 = d() >>> obj6.bar = 400 >>> obj6.baz = 500 >>> a.copy_into(obj6) >>> obj6.foo # set by a.copy_into 1 >>> obj6.bar # overridden by a.copy_into 2 >>> obj6.baz # untouched by a.copy_into 500 But attrs may also have modifiers as attribute values. When these are copied into a class or object, they modify any pre-existing value. Two of these operate on tuples: :class:`.extend` and :class:`.remove`. >>> from flask.ext.dry import extend, remove >>> a = attrs(foo=extend(4, 5), bar=remove(4, 6)) >>> obj5 = d() >>> obj5.foo = (1, 2, 3) >>> obj5.bar = (4, 5, 6) >>> a.copy_into(obj5) >>> obj5.foo (1, 2, 3, 4, 5) >>> obj5.bar (5,) Finally, :class:`.lookup` can be used to lookup a value at the time that the copy_into is done: >>> from flask.ext.dry import lookup >>> a = attrs(foo=lookup('x'), bar=lookup('y.z')) >>> obj5 = d() >>> obj5.x = 'first x value' >>> obj5.y = d() >>> obj5.y.z = 'first x.z value' >>> a.copy_into(obj5) >>> obj5.foo 'first x value' >>> obj5.bar 'first x.z value' >>> obj5.x = 'second x value' >>> obj5.y.z = 'second x.z value' >>> a.copy_into(obj5) >>> obj5.foo 'second x value' >>> obj5.bar 'second x.z value' You may create your own modifiers by deriving from :class:`.modifier`. Read the source code to see how to do this. .. _deepcopy: https://docs.python.org/3/library/copy.html#copy.deepcopy