object.__class__.__dict__ ! (python object model and friends) ! Robert Lujo, 2014
about me • software • professionally 17 y. • python since 2.0, django since 0.96 • freelancer • more info -> linkedin
OOP object oriented programming is a programming paradigm concept of "objects" a) "attributes" - data fields (state) b) methods - associated functions (logic) objects are instances of classes https://en.wikipedia.org/wiki/Object-oriented_programming
py - basic building blocks 1. expressions a + b! 2. functions - program logic, objects which can be "executed()" lambda a,b: a+b 3. objects (classes) Person() 4. "variables" - pointer to object per = Person() add = lambda a,b: a+b 5. commands for per in person_list:
everything is an object x = None def add(a,b): return a+b class Person(object): … ! for o in (1 , x, "", None, True, add, Person,Person(), Person().get_name ): print type(o),id(o),isinstance(o,object) ! <type 'int'> 140699668384744 True <type 'NoneType'> 4546336056 True <type 'str'> 4546876680 True <type 'NoneType'> 4546336056 True <type 'bool'> 4546257024 True <type 'function'> 4548133304 True <type 'type'> 140699671469632 True <class '__main__.Person'> 4547839824 True <type 'instancemethod'> 4547596112 True ! # https://docs.python.org/2.7/reference/datamodel.html
object types • two kinds: mutable and immutable • builtin immutable: int long float complex str/unicode bytes tuple frozenset • builtin mutable: list dict set byte-array
immutable • immutable can not be changed :) - check functional paradigm • important is to understand - "change" of variable is pointing to a new object ! >>> a = "test" >>> b = "test" >>> c = a >>> id("test"), id("test")==id(a)==id(b)==id(c) (4547809072, True) >>> a += "ing" # is this changing an object? >>> id(a), id(a)==id(b) # not really (4547808736, False)
mutable • supports changing the object in place ! >>> a, b = ["test"], ["test"] >>> c = a >>> id(a),id(b),id(c) (4548130576, 4547733192, 4548130576) >>> a.append(3) # promjena in-place >>> id(a),id(b),id(c) (4548130576, 4547733192, 4548130576) ! BUT - first member of both objects is immutable object and everywhere is the same! >>> id(a[0])==id(b[0])==id(c[0]) True
global and local • every context has its own global and local variables - use functions globals(), locals() • global variables are read-only, but again there is a difference between mutable and immutable • global keyword ! >>> g1, g2, g3 = 3, [3], 3 >>> id(g1), id(g2), id(g3) 2084115824 2523984 2084115824 ! def fun(): # print g1 # produces: SyntaxWarning: name 'g1' is used prior to global declaration global g1 print g1, g2, g3 # g3 = 4 # if this is enabled then previous "print g3" and raises an Error print "fg1:", id(g1), id(g2), id(g3) g1 = 4 # change of global variable to new object g2.append(4) # although g2 variable is read-only, the object is NOT! print "fg2:", id(g1), id(g2), id(g3) # g1 has changed id locally and globally ! >>> fun() 3 [3] 3 fg1: 2084115824 2523984 2084115824 fg2: 2084115812 2523984 2084115824 >>> print g1, g2, g3 4 [3, 4] 3 # change on g1 and g2 can be seen in object values >>> print id(g1), id(g2), id(g3) # the same as fg2: - function has changed global g1 2084115812 2523984 2084115824
custom classes • custom classes are mutable • all standard types can be inherited • for immutable types we can add methods and attribues, but we can't change internal object state (e.g. change value of int object "3" to have value 4) class Int(int): def add2(self): self.last = 2 return self+2 >>> x = Int(3) ; x.add2() ; x.last 5 2
custom classes • it is much more interesting to inherit mutable objects - e.g. ! class JavascriptDict(dict): def __getattr__(self, aname): if aname in self: return self[aname] raise AttributeError("Attr %s not found. Use %s" % ( aname, ",".join(self.keys()))) ! >>> d = JavascriptDict({"a" : 1, "b" : 2}) >>> d.a # calls d.__getattr__ - more details later … 1 >>> d.c AttributeError: Attr c not found. Use a,b
collections.* • from collections import OrderedDict! • records the order the items are added • from collections import Counter ! >>> p = Counter(blue=2, red=3) >>> p["green"]+=1 >>> p.most_common() [('red', 3), ('blue', 2), ('green', 1)] • from collections import defaultdict
collections.namedtuple >>> Person = namedtuple('Person', ['age', 'gender']) # returns class! >>> p = Person(20, "m") >>> p.age,p.gender -> 20, "m" >>> age, gender = p
misc std classes • ABC - handy for isinstance(): numbers.Number, basestring • currency type -> decimal.Decimal • UserDict, UserList - mutable dict and list - for inheritance
class, object and constructor >>> type(int) <type 'type'> ! >>> callable(int) True ! # class/type is a "function" which returns a new object >>> x = int(); print x, id(x) 0 14069966838468 ! # when and how was object "3" created? >>> int(3) == int.__call__(3) == 3 True ! >>> id(int(3)) == id(int.__call__(3)) == id(3) True
module loading & objects creation • execution on loading of a module - white is not executed import sys global_var = 3 # 2. maybe now object "3" is created? not IMHO. def get_add_nr(nr): # 3. create a function object - add2 not created def add2(a): return a+nr return add2 class Person(object): # 4. class object is getting created CLS_ATTR = 22 class Age(object): # 4.2 inner class object is getting created def get_age(self): # 4.2.1 creating function/object i.e. method raise NotImplementedError() def __init__(self,age,gender): # 4.3. creating function/object i.e. method self.age, self.gender = age,gender # dir() -> ['Person', 'get_add_nr', 'global_var', 'sys', '__file__', '__name__',…]
multiple inheritance & mixins • one class inherits more than one class • Mixin - popular usage of MI: mixin is a class which contains a combination of methods from other classes, … encourages code reuse ! class FormMixin(object): def init_fields(self): for i, (fname, field) in enumerate(self.fields.iteritems()): field.widget.attrs["placeholder"] = field.help_text ! class LoginForm(forms.Form, FormMixin): def __init__(self, *args, **kwargs): super(LoginForm, self).__init__(*args, **kwargs) self.init_fields()
MRO • method resolution order - which method will be called in the case of "method overloading"? ! class FormMixin(object): def init_fields(self): … # 3. mixin method ! class Form(object): def init_fields(self): … # 2. parent method ! class LoginForm(forms.Form, FormMixin): def init_fields(self): … # 1. own method def __init__(self, *args, **kwargs): self.init_fields() # 1. own method - by default super(LoginForm, self).init_fields() # 2. parent method FormMixin.init_fields(self) # 3. mixing method
obj/cls.__dict__ • useful in some cases - not the same as result of dir(obj/cls) • object.__dict__ - holds object attributes • class.__dict__ - holds class attributes and methods ! class Person(object): def __init__(self, name, dob): self.dob, self.name = dob, name ! >>> per = Person("Bero", date(2000,1,1)) >>> per.__dict__ # object content {'dob': datetime.date(2000, 1, 1), 'name': 'Bero'} >>> per.__class__.__dict__ # class / same as: Person.__dict__ {'__dict__': …, '__module__': '__main__', '__weakref__': …, '__doc__': None, '__init__': <function __init__ at 0x1051ea230>}
dynamic class creation • how to dynamically create an class, add attributes, methods … - runtime "code generator" ! def get_method(say): def meth1(self): print "%ss" % say # the last "s" is not an error return meth1 ! Cat = type('Cat', (object,), # it looks a bit like Js? {'meow': get_method('meow'), 'eat': get_method('eat')}) ! >>> c = Cat() >>> "Cat %s and %s" % (c.meow(), c.eat()) Cat meows and eats
class decorator • function which gets class object and returns (changed or different) class object ! def add_age_method(cls): def age_method(self): return date.today().year - self.dob.year cls.age = age_method return cos ! @add_age_method # is equal as: Person = add_age_method(Person) class Person(object): def __init__(self, dob): self.dob = dob >>> Person(date(2000,1,1)).age() 14
metaclasses • The most complex but also the most powerfull to manage class creation • django uses by implementing django.db.models.Model! ! class AddAgeMethodMeta(type): def __new__(cls, name, bases, attrs): def age_method(self): return date.today().year - self.dob.year attrs["age"] = age_method return super(AddAgeMethodMeta, cls).__new__(cls, name, bases, attrs) ! class Person(object): __metaclass__ = AddAgeMethodMeta # python 2 way def __init__(self, dob): self.dob = dob ! >>> Person(date(2000,1,1)).age() 14
@property • property - very frequently used in other languages - Java, C# • getter i setter - methods which "mimic" reading and changing an attribute • python @property decorator / function - example given is using explicit function call: class Person(object): def __init__(self, dob): self.dob = dob def get_age(self): return date.today().year - self.dob.year def set_age(self, new_age): self.dob = self.dob.replace(year=date.today().year - new_age) def del_age(self): raise AttributeError("Age can not be deleted") age = property(get_age, set_age, del_age, "pydoc - Age in years - based on diff d.o.b. - today") >>> p = Person(date(2000,1,1)) >>> p.age -> 14 # calls p.get_age() >>> p.age = 15 # calls p.set_age(15) >>> p.dob -> datetime.date(1999, 1, 1)
descriptors • generalization of @property decorator - to have one single place for the same type of property data • must be defined on class level! • used for: methods, django related, django deferred … This example is simple - descriptors are more powerful: ! class AgeProperty(object): def __get__(self,obj,objtype): return date.today().year - obj.dob.year ! class Person(object): age = AgeProperty() # must be defined on class level def __init__(self, dob): self.dob = dob ! >>> print Person(date(2000,1,1)).age # .age zone o.__class__.age.__get__(personobj) 14
context managers • generalization of prepare and cleanup of objects using with command + __enter__ & __exit__ methods • can be done with @contextlib.contextmanager decorator: class Person(object): DATA = {20 : dict(name="Katarina"), 23 : dict(name="Bero") } def __init__(self, name): self.name = name ! @contextlib.contextmanager def per_load_and_save(id): person = Person(**Person.DATA[id]) # load, on __enter__ yield person # send object in with blok (generator) Person.DATA[id] = dict(name=person.name) # save, on __exit__ with per_load_and_save(id=23) as person: print person.name person.name = "Jo"
__methods__ • there is a huge number of reserved __methods__ - only some interesting/practical will be listed • operators, commands and standard functions "." __getattr__,__setattr__... "for .. in" __iter__ [i] __getitem__,__setitem__... "if .. in" __contains__ len() __len__ int()… __int__,... "if "/bool() __nonzero__ +… __add__,... |… __and__,... >,[:]… ...
obj. => __getattr__, .. • "." operator • implementation using __getattr__, __setattr__, __delattr__ • much powerful alternative are __getattribute__ …. (doesn't check __dict__, "." recursion) ! class Person(object): def __init__(self, dob): self.dob = dob def __getattr__(self, aname): if aname=="age": return date.today().year - self.dob.year raise AttributeError("Attribute '%s' not found" % aname) ! >>> Person(date(2000,1,1)).age 14 !
"for obj in" => __iter__ • iteration -> implemenation of __iter__ method • usually returns generator (yield), but can be implemented with an iterator too (__iter__ + next) ! class Person(object): def __init__(self, name): self.name = name ! class PersonList(object): def __init__(self, data): self.data = data def __iter__(self): # yield -> function is a generator for d in self.data: yield Person(**d) >>> for person in PersonList([dict(name="Katarina"), dict(name="Bero")]): ... print person.name, Bero Katarina !
obj[i] => __getitem__, .. • [] operator - list or dictionary or … • implementing using __getitem__, __setitem__, __delitem__ ! class Person(object): def __init__(self, name): self.name = name ! class PersonList(object): def __init__(self, data): self.data = {d["name"] : Person(**d) for d in data} def __getitem__(self, name): return self.data[name] ! >>> per_list = PersonList([dict(name="Katarina"), dict(name="Bero")]) >>> print per_list["Bero"].name Bero
summary • python (IMHO) • has an object model which suites all common use cases and lot of special ones • hits a sweet spot when combining object and functional paradigm • encourages creating a nice API, readable code and maintainable program structure
aand … that's all folks! Thanks for the patience! Questions? Anyone? ! robert.lujo@gmail.com @trebor74hr

Object.__class__.__dict__ - python object model and friends - with examples

  • 1.
    object.__class__.__dict__ ! (python object modeland friends) ! Robert Lujo, 2014
  • 2.
    about me • software •professionally 17 y. • python since 2.0, django since 0.96 • freelancer • more info -> linkedin
  • 3.
    OOP object oriented programmingis a programming paradigm concept of "objects" a) "attributes" - data fields (state) b) methods - associated functions (logic) objects are instances of classes https://en.wikipedia.org/wiki/Object-oriented_programming
  • 4.
    py - basicbuilding blocks 1. expressions a + b! 2. functions - program logic, objects which can be "executed()" lambda a,b: a+b 3. objects (classes) Person() 4. "variables" - pointer to object per = Person() add = lambda a,b: a+b 5. commands for per in person_list:
  • 5.
    everything is anobject x = None def add(a,b): return a+b class Person(object): … ! for o in (1 , x, "", None, True, add, Person,Person(), Person().get_name ): print type(o),id(o),isinstance(o,object) ! <type 'int'> 140699668384744 True <type 'NoneType'> 4546336056 True <type 'str'> 4546876680 True <type 'NoneType'> 4546336056 True <type 'bool'> 4546257024 True <type 'function'> 4548133304 True <type 'type'> 140699671469632 True <class '__main__.Person'> 4547839824 True <type 'instancemethod'> 4547596112 True ! # https://docs.python.org/2.7/reference/datamodel.html
  • 6.
    object types • twokinds: mutable and immutable • builtin immutable: int long float complex str/unicode bytes tuple frozenset • builtin mutable: list dict set byte-array
  • 7.
    immutable • immutable cannot be changed :) - check functional paradigm • important is to understand - "change" of variable is pointing to a new object ! >>> a = "test" >>> b = "test" >>> c = a >>> id("test"), id("test")==id(a)==id(b)==id(c) (4547809072, True) >>> a += "ing" # is this changing an object? >>> id(a), id(a)==id(b) # not really (4547808736, False)
  • 8.
    mutable • supports changingthe object in place ! >>> a, b = ["test"], ["test"] >>> c = a >>> id(a),id(b),id(c) (4548130576, 4547733192, 4548130576) >>> a.append(3) # promjena in-place >>> id(a),id(b),id(c) (4548130576, 4547733192, 4548130576) ! BUT - first member of both objects is immutable object and everywhere is the same! >>> id(a[0])==id(b[0])==id(c[0]) True
  • 9.
    global and local •every context has its own global and local variables - use functions globals(), locals() • global variables are read-only, but again there is a difference between mutable and immutable • global keyword ! >>> g1, g2, g3 = 3, [3], 3 >>> id(g1), id(g2), id(g3) 2084115824 2523984 2084115824 ! def fun(): # print g1 # produces: SyntaxWarning: name 'g1' is used prior to global declaration global g1 print g1, g2, g3 # g3 = 4 # if this is enabled then previous "print g3" and raises an Error print "fg1:", id(g1), id(g2), id(g3) g1 = 4 # change of global variable to new object g2.append(4) # although g2 variable is read-only, the object is NOT! print "fg2:", id(g1), id(g2), id(g3) # g1 has changed id locally and globally ! >>> fun() 3 [3] 3 fg1: 2084115824 2523984 2084115824 fg2: 2084115812 2523984 2084115824 >>> print g1, g2, g3 4 [3, 4] 3 # change on g1 and g2 can be seen in object values >>> print id(g1), id(g2), id(g3) # the same as fg2: - function has changed global g1 2084115812 2523984 2084115824
  • 10.
    custom classes • customclasses are mutable • all standard types can be inherited • for immutable types we can add methods and attribues, but we can't change internal object state (e.g. change value of int object "3" to have value 4) class Int(int): def add2(self): self.last = 2 return self+2 >>> x = Int(3) ; x.add2() ; x.last 5 2
  • 11.
    custom classes • itis much more interesting to inherit mutable objects - e.g. ! class JavascriptDict(dict): def __getattr__(self, aname): if aname in self: return self[aname] raise AttributeError("Attr %s not found. Use %s" % ( aname, ",".join(self.keys()))) ! >>> d = JavascriptDict({"a" : 1, "b" : 2}) >>> d.a # calls d.__getattr__ - more details later … 1 >>> d.c AttributeError: Attr c not found. Use a,b
  • 12.
    collections.* • from collectionsimport OrderedDict! • records the order the items are added • from collections import Counter ! >>> p = Counter(blue=2, red=3) >>> p["green"]+=1 >>> p.most_common() [('red', 3), ('blue', 2), ('green', 1)] • from collections import defaultdict
  • 13.
    collections.namedtuple >>> Person =namedtuple('Person', ['age', 'gender']) # returns class! >>> p = Person(20, "m") >>> p.age,p.gender -> 20, "m" >>> age, gender = p
  • 14.
    misc std classes •ABC - handy for isinstance(): numbers.Number, basestring • currency type -> decimal.Decimal • UserDict, UserList - mutable dict and list - for inheritance
  • 15.
    class, object and constructor >>>type(int) <type 'type'> ! >>> callable(int) True ! # class/type is a "function" which returns a new object >>> x = int(); print x, id(x) 0 14069966838468 ! # when and how was object "3" created? >>> int(3) == int.__call__(3) == 3 True ! >>> id(int(3)) == id(int.__call__(3)) == id(3) True
  • 16.
    module loading &objects creation • execution on loading of a module - white is not executed import sys global_var = 3 # 2. maybe now object "3" is created? not IMHO. def get_add_nr(nr): # 3. create a function object - add2 not created def add2(a): return a+nr return add2 class Person(object): # 4. class object is getting created CLS_ATTR = 22 class Age(object): # 4.2 inner class object is getting created def get_age(self): # 4.2.1 creating function/object i.e. method raise NotImplementedError() def __init__(self,age,gender): # 4.3. creating function/object i.e. method self.age, self.gender = age,gender # dir() -> ['Person', 'get_add_nr', 'global_var', 'sys', '__file__', '__name__',…]
  • 17.
    multiple inheritance &mixins • one class inherits more than one class • Mixin - popular usage of MI: mixin is a class which contains a combination of methods from other classes, … encourages code reuse ! class FormMixin(object): def init_fields(self): for i, (fname, field) in enumerate(self.fields.iteritems()): field.widget.attrs["placeholder"] = field.help_text ! class LoginForm(forms.Form, FormMixin): def __init__(self, *args, **kwargs): super(LoginForm, self).__init__(*args, **kwargs) self.init_fields()
  • 18.
    MRO • method resolutionorder - which method will be called in the case of "method overloading"? ! class FormMixin(object): def init_fields(self): … # 3. mixin method ! class Form(object): def init_fields(self): … # 2. parent method ! class LoginForm(forms.Form, FormMixin): def init_fields(self): … # 1. own method def __init__(self, *args, **kwargs): self.init_fields() # 1. own method - by default super(LoginForm, self).init_fields() # 2. parent method FormMixin.init_fields(self) # 3. mixing method
  • 19.
    obj/cls.__dict__ • useful insome cases - not the same as result of dir(obj/cls) • object.__dict__ - holds object attributes • class.__dict__ - holds class attributes and methods ! class Person(object): def __init__(self, name, dob): self.dob, self.name = dob, name ! >>> per = Person("Bero", date(2000,1,1)) >>> per.__dict__ # object content {'dob': datetime.date(2000, 1, 1), 'name': 'Bero'} >>> per.__class__.__dict__ # class / same as: Person.__dict__ {'__dict__': …, '__module__': '__main__', '__weakref__': …, '__doc__': None, '__init__': <function __init__ at 0x1051ea230>}
  • 20.
    dynamic class creation •how to dynamically create an class, add attributes, methods … - runtime "code generator" ! def get_method(say): def meth1(self): print "%ss" % say # the last "s" is not an error return meth1 ! Cat = type('Cat', (object,), # it looks a bit like Js? {'meow': get_method('meow'), 'eat': get_method('eat')}) ! >>> c = Cat() >>> "Cat %s and %s" % (c.meow(), c.eat()) Cat meows and eats
  • 21.
    class decorator • functionwhich gets class object and returns (changed or different) class object ! def add_age_method(cls): def age_method(self): return date.today().year - self.dob.year cls.age = age_method return cos ! @add_age_method # is equal as: Person = add_age_method(Person) class Person(object): def __init__(self, dob): self.dob = dob >>> Person(date(2000,1,1)).age() 14
  • 22.
    metaclasses • The mostcomplex but also the most powerfull to manage class creation • django uses by implementing django.db.models.Model! ! class AddAgeMethodMeta(type): def __new__(cls, name, bases, attrs): def age_method(self): return date.today().year - self.dob.year attrs["age"] = age_method return super(AddAgeMethodMeta, cls).__new__(cls, name, bases, attrs) ! class Person(object): __metaclass__ = AddAgeMethodMeta # python 2 way def __init__(self, dob): self.dob = dob ! >>> Person(date(2000,1,1)).age() 14
  • 23.
    @property • property -very frequently used in other languages - Java, C# • getter i setter - methods which "mimic" reading and changing an attribute • python @property decorator / function - example given is using explicit function call: class Person(object): def __init__(self, dob): self.dob = dob def get_age(self): return date.today().year - self.dob.year def set_age(self, new_age): self.dob = self.dob.replace(year=date.today().year - new_age) def del_age(self): raise AttributeError("Age can not be deleted") age = property(get_age, set_age, del_age, "pydoc - Age in years - based on diff d.o.b. - today") >>> p = Person(date(2000,1,1)) >>> p.age -> 14 # calls p.get_age() >>> p.age = 15 # calls p.set_age(15) >>> p.dob -> datetime.date(1999, 1, 1)
  • 24.
    descriptors • generalization of@property decorator - to have one single place for the same type of property data • must be defined on class level! • used for: methods, django related, django deferred … This example is simple - descriptors are more powerful: ! class AgeProperty(object): def __get__(self,obj,objtype): return date.today().year - obj.dob.year ! class Person(object): age = AgeProperty() # must be defined on class level def __init__(self, dob): self.dob = dob ! >>> print Person(date(2000,1,1)).age # .age zone o.__class__.age.__get__(personobj) 14
  • 25.
    context managers • generalizationof prepare and cleanup of objects using with command + __enter__ & __exit__ methods • can be done with @contextlib.contextmanager decorator: class Person(object): DATA = {20 : dict(name="Katarina"), 23 : dict(name="Bero") } def __init__(self, name): self.name = name ! @contextlib.contextmanager def per_load_and_save(id): person = Person(**Person.DATA[id]) # load, on __enter__ yield person # send object in with blok (generator) Person.DATA[id] = dict(name=person.name) # save, on __exit__ with per_load_and_save(id=23) as person: print person.name person.name = "Jo"
  • 26.
    __methods__ • there isa huge number of reserved __methods__ - only some interesting/practical will be listed • operators, commands and standard functions "." __getattr__,__setattr__... "for .. in" __iter__ [i] __getitem__,__setitem__... "if .. in" __contains__ len() __len__ int()… __int__,... "if "/bool() __nonzero__ +… __add__,... |… __and__,... >,[:]… ...
  • 27.
    obj. => __getattr__,.. • "." operator • implementation using __getattr__, __setattr__, __delattr__ • much powerful alternative are __getattribute__ …. (doesn't check __dict__, "." recursion) ! class Person(object): def __init__(self, dob): self.dob = dob def __getattr__(self, aname): if aname=="age": return date.today().year - self.dob.year raise AttributeError("Attribute '%s' not found" % aname) ! >>> Person(date(2000,1,1)).age 14 !
  • 28.
    "for obj in"=> __iter__ • iteration -> implemenation of __iter__ method • usually returns generator (yield), but can be implemented with an iterator too (__iter__ + next) ! class Person(object): def __init__(self, name): self.name = name ! class PersonList(object): def __init__(self, data): self.data = data def __iter__(self): # yield -> function is a generator for d in self.data: yield Person(**d) >>> for person in PersonList([dict(name="Katarina"), dict(name="Bero")]): ... print person.name, Bero Katarina !
  • 29.
    obj[i] => __getitem__,.. • [] operator - list or dictionary or … • implementing using __getitem__, __setitem__, __delitem__ ! class Person(object): def __init__(self, name): self.name = name ! class PersonList(object): def __init__(self, data): self.data = {d["name"] : Person(**d) for d in data} def __getitem__(self, name): return self.data[name] ! >>> per_list = PersonList([dict(name="Katarina"), dict(name="Bero")]) >>> print per_list["Bero"].name Bero
  • 30.
    summary • python (IMHO) •has an object model which suites all common use cases and lot of special ones • hits a sweet spot when combining object and functional paradigm • encourages creating a nice API, readable code and maintainable program structure
  • 31.
    aand … that'sall folks! Thanks for the patience! Questions? Anyone? ! robert.lujo@gmail.com @trebor74hr