Object oriented programming in python

Post on 14-Jan-2017

503 views 2 download

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: radoslaw@ganczarek.intwitter: @dreamwalker_in