Date post: | 28-Nov-2014 |
Category: |
Software |
Upload: | robert-lujo |
View: | 462 times |
Download: | 1 times |
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
OOPobject 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 blocks1. 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 objectx = 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
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