Object oriented programming in python
-
Upload
radoslaw-jan-ganczarek -
Category
Technology
-
view
503 -
download
2
Transcript of Object oriented programming in python
Object-oriented programming in Python
Radosław Ganczarek
Important links
This presentation: http://bit.ly/1U6EFDDGist with examples: http://bit.ly/1fGkKIC
Object-oriented programming
Everything is an object?
Don’t forget the diagrams...
But hey, we are coding Python!
Mr Chicken as a classclass Chicken(object): "Describes a chicken." def __init__(self, name): self.name = name def make_sounds(self): "Print chicken sound." print "squeaaaak!"
But in the real world...class WorkflowProxy(object): def __init__(self, identity, f): self.identity = identity self.f = f
def __call__(self, decision, history, tracer): th = TaskHistory(history, self.identity) wd = WorkflowDecision(decision, self.identity, self.f) if tracer is None: return Proxy(th, wd) return TracingProxy(tracer, self.identity, th, wd)
Let’s start!
Basics
Mr Chicken, welcome backclass Chicken(object): """Describes a chicken.""" def __init__(self, name): self.name = name
def make_sounds(self): """Print chicken sound.""" print "squeaaaak!"
What can we do with this class?# Make and use instancemr_chicken = Chicken('mr')mr_chicken.make_sounds()
# See docsprint Chicken.__doc__print Chicken.make_sounds.__doc__
# Weird, but worksChicken.make_sounds(mr_chicken)
Attributesclass Shop(object): first_shop_in_town = None
def __init__(self, name): if not Shop.first_shop_in_town: Shop.first_shop_in_town = name self.name = name
shop1 = Shop('ladybug')shop2 = Shop('frog')print Shop.first_shop_in_town
Lesson, lesson!Write a class Game, which will contain information about a football game. You should be abble to:● add new game (names of teams, results, etc.)● update game with new data, assuming you’ve created game
object before game started and you want to add new goals during the match (but some data, like teams, won’t change)
● check number of games added so far● (not mandatory) write simple console interface for managing
games
More on attributes
Dynamic attribute accessmr_chicken = Chicken('mr')mr_chicken.driver_license_number = '666999666'
for field in ['name', 'tshirt_size', 'driver_license_number']: if hasattr(mr_chicken, field): if field == 'driver_license_number': print "Chrickens are not allowed to drive!" delattr(mr_chicken, field) else: print '{0}: {1}'.format(field, getattr(mr_chicken, field)) else: print "Empty field {}".format(field) setattr(mr_chicken, field, 'here be nothing')
Magic methods
__getattr____setattr____delattr____getattr__ vs __getattribute__
Let’s see all attributes!mr_chicken = Chicken('mr')
print mr_chicken.__dict__
print dir(mr_chicken)
print Chicken.__dict__
Meet the garbage man!
del mr_chicken # It's not deletion of object!
Lesson, lesson!Write class Roman, which will implement conversion from roman number system. Save numbers you convert once so that you don’t have to compute them second time.Example usage:r = Roman()
print r.xvi
>>> 16
Hint: http://bit.ly/1K37YSN
Inheritance
class Article(object): title = None body = None
def __init__(self, **kwargs): for key, value in kwargs.iteritems(): if hasattr(self, key): setattr(self, key, value)
def publish(self): print self.title print self.body
class SponsoredArticle(Article): company = None product = None
def publish(self): print "A nice article from {0}, creators of {1}!".format( self.company, self.product, ) super(SponsoredArticle, self).publish()
docker_howto = Article(title="Docker HOWTO", body="Just use docker")google_it = SponsoredArticle(title="Google it!", body="To google or not to google.", company="Alphabet", product="Google")
print google_it.company
Finding outprint isinstance(google_it, Article)print isinstance(docker_howto, SponsoredArticle)print issubclass(SponsoredArticle, Article)print issubclass(SponsoredArticle, object)print repr(google_it) # __repr__print str(docker_howto) # __str__print docker_howto.__class__.__name__
Hide and protectclass A(object): _name = None __id = None
class B(A): passa = A()A._name = 'Daisy'print B._name # Avoidb = B()print b._name # Avoidprint b.__id # Error!print b._A__id # Not error, but still bad
Lesson, lesson!Write a a couple (at least two) of classes inherinting from Game to store results of other games, that will store different data (e.g. number of sets in volleyball, names instead of teams in tennis, number of red cards in football).Update and overload methods.Hide information that should not be seen
Operators
Overloading operatorsUnary:__pos__ +x__neg__ -x__abs__ abs(x)__round__ round(x)__floor__ math.floor(x)__ceil__ math.ceil(x)
Binary:__add__ x + y__sub__ x - y__mul__ x * y__div__ x / y__mod__ x % y__pow__ x ** y__cmp__ *comparison
Exampleclass Railway(object): def __init__(self, length, income): self.length = length self.income = income
def __cmp__(self, other): if abs(self.length - other.length) <= 100: return cmp(self.income, other.income) return cmp(self.length, other.length)
def __add__(self, other): return Railway( self.length + other.length, self.income + other.income - 100, )
Examplepkp = Railway(1000, 200)pendolino = Railway(2000, 100)tgv = Railway(1100, 100)
print pkp < tgvprint tgv < pendolinopkp_pendolino = pkp + pendolinoprint pkp_pendolino.__dict__
Lesson, lesson!
Create class Team for football teams.Make it possible to compare teams.Implement operator * for teams. Its result will be Game object.Try to do similar stuff for other kinds of games you implemented.
Decoratorsclass Options(object): loaded_files = [] options = None
@staticmethod def parse(filename): <parse file>
def load_options(self, filename): self.options=dict(self.parse(filename)) Options.loaded_files.append(filename)
@classmethod def was_analyzed(cls, filename):
return filename in cls.loaded_files
@property def option_keys(self): return self.options.keys()
o = Options()o.load_options('/etc/profile')print Options.was_analyzed('/etc/profile')print o.option_keys
Identitya = 1b = 1print a is ba = 257b = 257print a is b
l1 = [1,2,3]l2 = [1,2,3]print l1 is l2print id(l1)
__repr__ vs __str__ vs __hash__
Everything is an object, even a class
Everything is an objecta = 1
print dir(a)
class BetterInt(int):
pass
Chicken = type('Chicken', (object, ), {'name': None})
print type(Chicken)
an_object = object()
Types
Type
Can’t be changedOperations supportedValues supportedMutabilityComparison is available for all objects (but it doesn’t mean it’s sane)https://docs.python.org/2/library/stdtypes.html
Small types
import typesmy_one = types.NoneType() # Won't work!my_not_implemented = types.NotImplementedType() # Won't work!
Type Instances Boolean value
types.NoneType None False
types.NotImplementedType NotImplemented True
Numeric types
Protocol: all arithmetic and bitwise operatorsProtocol for bool: logic operatorsProtocol for real: floor, ceil, round, trunc
Iteratorsfrom random import randintclass RandomIterator(object): count = 0
def __iter__(self): return self
def next(self): if self.count > 7: raise StopIteration() self.count += 1 return randint(0, 256)
randomiter = (randint(0, 256) for _ in range(7))
print RandomIterator().next()print randomiter.next()
Context Managersclass ReadConfigFile(object): def __init__(self, filename): self.filename = filename
def _parse(self): return {'hostname': self.fd.read()}
def __enter__(self): self.fd = open(self.filename, 'r') return self._parse()
def __exit__(self, exc_type, exc_val, exc_tb): self.fd.close()
with ReadConfigFile('/etc/hostname') as options: print options['hostname']
Lesson, lesson!
1. Implement iterator with numbers written as roman numerals.
2. Create context manager WorldCup that works like this
with WorldCup(): game = team1 * team2 print type(game) # WorldCupGame
game2 = team1 * team2print type(game) # Game
hint - use global
Nice, but what’s with it “everything is an object”?
Callable objects
FunctionsMethodsClass typesBuilt-in functionsBuilt-in methodsGenerators...
def function(x=1): "Blah" a = 1
print dir(function)print function.func_codeprint function.func_defaultsprint function.func_globalsprint function.func_name
function = lambda x: xprint function.func_name
Internal objects
CodeFrameTracebackSliceStatic methodClass method
Internal class manipulation
__new__ vs __init__
Is a static methodTakes class as first argumentReturns a classIs ran before __init__Creates class
Is an instance methodTakes instance as first argumentShould return NoneIs ran after __new__Customises instance
Metaclassesdef metacls(name, bases, dict): for key, value in dict.items(): if isinstance(value, int): dict[key] = float(value) return type(name, bases, dict)
class A(object): a = 1 __metaclass__ = metacls
a = A()print a.a
When creating class A instead of running:type( 'A', (object,), {'a': 1})this is run:metacls( 'A', (object,), {'a': 1},)
Faking roots
Remember isinstance and issubclass? You can trick them with:__isinstancecheck____subclasscheck__
Faking behaviour
Behave like:- function- dict- collection- list
__call____*item____len__ __contains____*slice__
Lesson, lesson!
Modify class Team to contain a list of all players with their numbers. Make it possible to access a player of number n by calling: t[n].Add metaclass to Game classes, that will take first players from both teams and save them in additional field “captains” as a mapping between team name and captain name.
Not tired of attributes? Here’s more!
Descriptors (__getattribute__)a.xa.__dict__['x']type(x).__dict__['x']...
A.xA.__dict__['x']...
a.xtype(a).__dict__['x'].__get__( a, type(a),)
A.xA.__dict__['x'].__get__( None, A,)
Kinds of descriptors
Data descriptorNon-data descriptorRead-only data descriptor
__get__, __set____get____get__, __set__ (raises AtrributeError)
__delete__
Quirksclass A(object):
@propertydef get(self):
return 1
@staticmethoddef show():
return 'a'
a = A()a.__dict__['get'] = 2print a.geta.__dict__['show'] = 'b'print a.show
More descriptorsfrom collections import defaultdict
class AttrStat(object):_stats = defaultdict(int)
def __init__(self, value):
self.value = value
def __get__(self, obj, objtype): self._stats['get'] += 1 return self.value
def __set__(self, obj, val): self._stats['set'] += 1 self.value = val
class A(object):x = AttrStat(1)
a = A()print a.xa.x = 100print a.xprint dict(AttrStat._stats)
Remember property decorator?
It’s a descriptor!
property(fget, fset, fdel, doc)
property.getter
property.setter
property.deleter
Slotsfrom sys import getsizeofclass A(object):
def __init__(self): self.x = 1
class B(object):__slots__ = ('x', )def __init__(self):
self.x = 1print getsizeof(A), getsizeof(B)print getsizeof(A()), getsizeof(B())
Important notes
Inheriting class without slots will still make its __dict__ accessibleYou cant add new attributesSlots use descriptors (is it surprising???)__slots__ is not shared to subclassesoverloaded slot is not accessible
Abstract Base classes
ABCMetaregisterabstractmethodabstractproperty
ABC Examplefrom abc import *
class Animal(object): __metaclass__ = ABCMeta @abstractmethod def make_sounds(self): pass @abstractproperty def appearance(self): pass
class Dog(object): fur_color = None def make_sounds(self): print "Bark!" def appearance(self): print '{} dog'.format(self.fur_color) class Chicken(object): feather_colour = None def make_sounds(self): print "squeak!" def appearance(self): print "{} chicken".format(self.feather_colour) Animal.register(Dog)Animal.register(Chicken)
MRO
Lesson, lesson!
Implement abstract class for game classes.
Make descriptor RomanNumber that works like this
class A(object): n = RomanNumber(8)
a = A()print a.n# VIIIa.n = 19print a.n# XIX
Thank you!
Radosław GanczarekSenior Python Developer @ STX Next
email: [email protected]: @dreamwalker_in