You are on page 1of 23

PyCon2015PythonEpiphanies

Overview
Thistutorial,presentedatPyCon2015inMontrealbyStuartWilliams,isintendedforintermediatePythonuserslookingfora
deeperunderstandingofthelanguage.ItattemptstocorrectsomecommonmisperceptionsofhowPythonworks.Pythonis
verysimilartootherprogramminglanguages,butquitedifferentinsomesubtlebutimportantways.
You'lllearnbyseeinganddoing.We'llmostlyusetheinteractivePythoninterpreterprompt,akatheReadEvalPrintLoop
(REPL).I'llbeusingPythonversion3.4butmostofthiswillworkidenticallyinearlierversions.
Mostexercisesectionsstartoutsimplebutincreasequicklyindifficultyinordertogivemoreadvancedstudentsachallenge,so
don'texpecttofinishalltheexercisesineachsection.Iencourageyoutorevisitthemlater.
Iencourageyoutonotcopyandpastecodefromthisdocumentwhenyoudotheexercises.Bytypingthecodeyouwilllearn
more.AlsopausebeforeyouhittheEnterkeyandtrytopredictwhatwillhappen.
License:ThisPyCon2015PythonEpiphaniesTutorialbyStuartWilliamsislicensedunderaCreativeCommonsAttribution
ShareAlike2.5CanadaLicense(http://creativecommons.org/licenses/bysa/2.5/ca/).
PythonEpiphaniesTutorial
PresentedatPyCon2015
April8th,2015
Montreal,Quebec,Canada
StuartWilliams
stuart@swilliams.ca
@ceilous

Objects
>>>#Createobjectsvialiterals0
>>>11
>>>3.142
>>>'walk'3
>>>#Precreatedobjectsviaconstants4
>>>True5
>>>False6

EverythinginPython(atruntime)isanobjectandhas:
asinglevalue,
asingletype,
somenumberofattributes,
oneormorebaseclasses,
asingleid,and
(zeroor)oneormorenames(inoneormorenamespaces).
>>>#Objecthavetypes7
>>>type(1)8
>>>type(3.14)9
>>>type('walk')10
>>>type(True)11
>>>#Objectshaveattributes12
>>>True.__doc__13
>>>'walk'.__add__14
>>>callable('walk'.__add__)15
>>>'walk'.__add__('about')16
>>>(2.0).hex17

>>>(2.0).hex()18
>>>(4.0).hex()19
>>>#Objectshavebaseclasses20
>>>importinspect21
>>>inspect.getmro(3)22
>>>inspect.getmro(type(3))23
>>>inspect.getmro(type('walk'))24
>>>inspect.getmro(type(True))25
>>>#Baseclassesarestoredinattributes26
>>>True.__class__27
>>>True.__class__.__bases__28
>>>True.__class__.__bases__[0]29
>>>True.__class__.__bases__[0].__bases__[0]30
>>>inspect.getmro(type(True))31
>>>#Everyobjecthasoneid(memoryaddressinCPython)32
>>>id(3)33
>>>id(3.14)34
>>>id('walk')35
>>>id(True)36
>>>#Createobjectsbycallinganobject(function,method,class)37
>>>abs38
>>>callable(abs)39
>>>abs(3)40
>>>int41
>>>callable(int)42
>>>int(3.14)43
>>>'walk'.__len__44
>>>'walk'.__len__()45
>>>'walk'.__add__46
>>>'walk'.__add__('about')47
>>>dict48
>>>dict()49
>>>dict(pi=3.14,e=2.71)50

Exercises:Objects
>>>5.051
>>>dir(5.0)52
>>>5.0.__add__53
>>>callable(5.0.__add__)54
>>>5.0.__add__()55
>>>5.0.__add__(4)56
>>>4.__add__57
>>>(4).__add__58
>>>(4).__add__(5)59
>>>importsys60
>>>size=sys.getsizeof61
>>>size('w')62
>>>size('walk')63
>>>size(2)64
>>>size(2**301)65
>>>size(2**30)66
>>>size(2**601)67
>>>size(2**60)68
>>>size(2**1000)69

Names
>>>#Wecanaddnamestorefertoobjects70
>>>#Addingnamestoanamespaceislikeupdatingadictionary71
>>>dir()72
>>>def__names():73

...returndict([(k,v)for(k,v)inglobals().items()
...ifnotk.startswith('__')])
>>>__names()74
>>>a75
>>>a=30076
>>>__names()77
>>>a78
>>>a=40079
>>>__names()80
>>>a81
>>>b=a82
>>>b83
>>>a84
>>>__names()85
>>>id(a)86
>>>id(b)87
>>>aisb88
>>>a='walk'89
>>>a90
>>>b91
>>>dela92
>>>__names()93
>>>delb94
>>>#objectattributesarelikedictionariesofdictionaries95
>>>my_namespace={}96
>>>my_namespace['r']={}97
>>>my_namespace['r']['x']=1.098
>>>my_namespace['r']['y']=2.099
>>>my_namespace['r']['x']100
>>>my_namespace['r']101
>>>my_namespace102
>>>#ForPython<3.3useclassSimpleNamespace:pass103
>>>importtypes104
>>>r=types.SimpleNamespace()105
>>>r.x=1.0106
>>>r.y=1.0107
>>>r.x108
>>>r.y109
>>>#'is'checksidentity(via'id'),notequality110
>>>i=10111
>>>j=10112
>>>iisj113
>>>i=500114
>>>j=500115
>>>iisj116
>>>#CPythonspecificoptimizations117
>>>id(254)118
>>>id(255)119
>>>id(256)120
>>>id(257)121
>>>id(258)122

Exercises:Names
RestartPythontounclutterthelocalnamespace.
>>>i123
>>>dir()124
>>>i=1125
>>>i126
>>>dir()127
>>>type(i)128
>>>j=i129
>>>iisj130

>>>m=[1,2,3]131
>>>m132
>>>n=m133
>>>n134
>>>dir()135
>>>misn136
>>>m[1]='two'137
>>>m138
>>>n139
>>>s=t='hello'140
>>>s141
>>>sist,id(s),id(t)142
>>>s+='there'143
>>>s144
>>>sist,id(s),id(t)145
>>>m=n=[1,2,3]146
>>>m147
>>>misn,id(m),id(n)148
>>>m+=[4]149
>>>m150
>>>n151
>>>misn,id(m),id(n)152
>>>int.__add__153
>>>int.__add__=int.__sub__154
>>>new_object=object()155
>>>dir(None)156
>>>len(dir(None)),len(dir(new_object))157
>>>set(dir(None))set(dir(new_object))158
>>>importsys159
>>>refs=sys.getrefcount160
>>>refs(None)161
>>>refs(object)162
>>>refs(new_object)163
>>>sentinel=object()164
>>>sentinel==object()165
>>>sentinel==sentinel166
>>>refs(1)167
>>>refs(2)168
>>>refs(25)169
>>>[sys.getrefcount(i)foriinrange(266)]170

Namespaces
Anamespaceisamappingfromvalididentifiernamestoobjects.Thinkofitasadictionary.
Assignmentisanamespaceoperation,notanoperationonvariablesorobjects.
AscopeisasectionofPythoncodewhereanamespaceisdirectlyaccessible.
Foradirectlyaccessiblenamespaceyouaccessvaluesinthe(namespace)dictionarybykeyalone,e.g.s2insteadof
my_namespace['s2'].
Forindirectlyaccessiblenamespaceyouaccessvaluesviadotnotation,e.g.dict.__doc__orsys.version_info.major.
The(direct)namespacesearchorderis(fromhttp://docs.python.org/tutorial):
#1:theinnermostscopecontainslocalnames
#2:thenamespacesofenclosingfunctions,searchedstartingwiththenearestenclosingscope(orthemoduleifoutside
anyfunction)
#3:themiddlescopecontainsthecurrentmodule'sglobalnames
#4:theoutermostscopeisthenamespacecontainingbuiltinnames
Allnamespacechangeshappeninthelocalscope(i.e.inthecurrentscopeinwhichthenamespacechangingcodeexecutes):

=i.e.assignment
del
import
def
class

Othernamespacechanges:
functionparameters:deffoo(NEW_NAME):
forloop:forNEW_NAMEin...
exceptclause:ExceptionasNEW_NAME:
withclause:withopen(filename)asNEW_NAME:
docstrings:__doc__
>>>len171
>>>deff1():172
...deflen():
...len=range(3)
...print("Inf1'slen(),len={}".format(len))
...return'Returninglen:{!r}'.format(len)
...print('Inf1(),len={}'.format(len))
...returnlen()
>>>f1()173
>>>deff2():174
...deflen():
...#len=range(3)
...print("Inf2'slen(),len={}".format(len))
...return'Returninglen:{!r}'.format(len)
...print('Inf2(),len={}'.format(len))
...returnlen()
>>>f2()175
>>>len176
>>>len=99177
>>>deff3(s):178
...print('len(s)=={}'.format(len(s)))
>>>f3('walk')179
>>>len180
>>>dellen181
>>>len182
>>>f3('walk')183
>>>pass184
>>>pass=3185
>>>del186

Keywords:
Falseclassfinallyisreturn
Nonecontinueforlambdatry
Truedeffromnonlocalwhile
anddelglobalnotwith
aselififoryield
assertelseimportpass
breakexceptinraise

Namespaces:functionlocals
Let'slookatsomesurprisingbehaviour:
>>>x=1187
>>>deftest1():188
...print('Intest1x==',x)
>>>test1()189
>>>deftest2():190
...x=2

...print('Intest2x==',x)
>>>x191
>>>test2()192
>>>x193
>>>deftest3():194
...print('Intest3==',x)
...x=3
>>>x195
>>>test3()196
>>>x197
>>>test3.__code__198
>>>test3.__code__.co_argcount199
>>>test3.__code__.co_name200
>>>test3.__code__.co_names201
>>>test3.__code__.co_nlocals202
>>>test3.__code__.co_varnames#tupleoflocalnames203

"Ifanameisdeclaredglobal,thenallreferencesandassignmentsgodirectlytothemiddlescopecontainingthemodule's
globalnames.Otherwise,allvariablesfoundoutsideoftheinnermostscopearereadonly(anattempttowritetosucha
variablewillsimplycreateanewlocalvariableintheinnermostscope,leavingtheidenticallynamedoutervariable
unchanged)."[Pythontutorialsection9.2athttp://docs.python.org/tutorial]
>>>deftest4():204
...globalx
...print('Intest4before,x==',x)
...x=4
...print('Intest4after,x==',x)
>>>x205
>>>test4()206
>>>x207
>>>test4.__code__.co_varnames208
>>>deftest5():209
...x=5
...deftest6():
...nonlocalx
...print('test6beforex==',x)
...x=6
...print('test6afterx==',x)
...print('test5beforex==',x)
...test6()
...print('test5afterx==',x)
>>>x=1210
>>>x211
>>>test5()212
>>>x213

TheLocalNamespace
>>>help(dir)214
>>>dir()215
>>>importbuiltins,collections,inspect,textwrap216
>>>fill=textwrap.TextWrapper(width=60).fill217
>>>defpfill(pairs):218
...print(fill(''.join(
...(nfor(n,o)insorted(pairs)))))
>>>members=set([219
...mformininspect.getmembers(builtins)
...ifnotm[0].startswith('_')])
>>>len(members)220
>>>exceptions=[221

...(name,obj)for(name,obj)inmembers
...ifinspect.isclass(obj)and
...issubclass(obj,BaseException)]
>>>members=set(exceptions)222
>>>len(exceptions)223
>>>pfill(exceptions)224
>>>len(members)225
>>>pfill(members)226
>>>type(int)227
>>>type(len)228
>>>bnames=collections.defaultdict(set)229
>>>forname,objinmembers:230
...bnames[type(obj)].add((name,obj))
>>>fortypin[type(int),type(len)]:231
...pairs=bnames.pop(typ)
...print(typ)
...pfill(pairs)
...print()
>>>fortyp,pairsinbnames.items():232
...print('{}:{}'.format(typ,''.join((nfor(n,o)inpairs))))

Exercises:Namespaces
>>>locals().keys()233
>>>globals().keys()234
>>>locals()==globals()235
>>>locals()isglobals()#NotalwaysTrue236
>>>x237
>>>locals()['x']238
>>>locals()['x']=1239
>>>locals()['x']240
>>>x241
>>>dir()242

Mostbuiltinsareunsurprisingcasesoftypeexception,typebuiltinfunction,ortype.Exploresomeofthefollowing
suprisingonesviaintrospection(e.g.type,inspect.getmro,andhelp)orthePythondocumentation:
...
Ellipsis
NotImplementedType
True,None
>>>importinspect243
>>>inspect.getmro(type(True))244

NamespaceChanges
Remember,thesechangeormodifyanamespace:
assignment
[globals()andlocals()]
import
def
class
del
[alsodef,for,except,with,docstrings]

Nextwe'llexploreimport.

>>>dir()245
>>>importpprint246
>>>dir()247
>>>pprint248
>>>dir(pprint)249
>>>print('\n'.join([aforaindir(pprint)250
...ifnota.startswith('_')]))
>>>pprint.pformat251
>>>pprint.pprint252
>>>pprint.foo253
>>>foo254
>>>pprint.foo='Pythonisdangerous'255
>>>pprint.foo256
>>>frompprintimportpformataspprint_pformat257
>>>dir()258
>>>pprint.pformatispprint_pformat259
>>>pprint260
>>>pprint.pformat261
>>>delpprint262
>>>importpprintaspprint_module263
>>>dir()264
>>>pprint_module.pformatispprint_pformat265
>>>module_name='string'266
>>>importimportlib267
>>>string_module=importlib.import_module(module_name)268
>>>string_module.ascii_uppercase269
>>>string270
>>>importmodule_name271
>>>import'string'272
>>>importstring273

Filestructure:
folder1/
file1.py
module1/
__init__.pyzerolength
file1.py:
attribute1=1
>>>dir()274
>>>importfolder1275
>>>dir(folder1)276
>>>hasattr(folder1,'__path__')277
>>>importfolder1.file1278
>>>dir(folder1.file1)279
>>>importmodule1280
>>>dir()281
>>>dir(module1)282
>>>importmodule1.file1283
>>>dir()284
>>>dir(module1)285
>>>dir(module1.file1)286
>>>frommodule1importfile1287
>>>dir()288
>>>dir(file1)289

Exercises:Theimportstatement
>>>importpprint290
>>>dir(pprint)291
>>>pprint.__doc__292
>>>pprint.__file__293
>>>pprint.__name__294
>>>pprint.__package__295

>>>frompprintimport*296
>>>dir()297
>>>importimportlib298
>>>importlib.reload(csv)299
>>>importlib.reload('csv')300
>>>importcsv301
>>>importlib.reload('csv')302
>>>importlib.reload(csv)303
>>>importsys304
>>>sys.path305

Functions
>>>deff():306
...pass
>>>f.__name__307
>>>dir()308
>>>f.__name__='g'309
>>>dir()310
>>>f.__name__311
>>>f312
>>>f.__qualname__#Python>=3.3313
>>>f.__qualname__='g'314
>>>f315
>>>f.__dict__316
>>>f.foo='bar'317
>>>f.__dict__318
>>>deff(a,b,k1='k1',k2='k2',319
...*args,**kwargs):
...print('a:{!r},b:{!r},'
...'k1:{!r},k2:{!r}'
....format(a,b,k1,k2))
...print('args:',repr(args))
...print('kwargs:',repr(kwargs))
>>>f.__defaults__320
>>>f(1,2)321
>>>f(a=1,b=2)322
>>>f(b=1,a=2)323
>>>f(1,2,3)324
>>>f(1,2,k2=4)325
>>>f(1,k1=3)326
>>>f(1,2,3,4,5,6)327
>>>f(1,2,3,4,keya=7,keyb=8)328
>>>f(1,2,3,4,5,6,keya=7,keyb=8)329
>>>defg(a,b,*args,c=None):330
...print('a:{!r},b:{!r},'
...'args:{!r},c:{!r}'
....format(a,b,args,c))
>>>g.__defaults__331
>>>g.__kwdefaults__332
>>>g(1,2,3,4)333
>>>g(1,2,3,4,c=True)334
>>>defh(a=None,*args,b=None):335
...print('a:{!r},args:{!r},'
...'b:{!r}'
....format(a,args,b))
>>>h.__defaults__336
>>>h.__kwdefaults__337
>>>h(1,2,3,4)338
>>>h(1,2,3,4,b=True)339

Exercises:Functions
>>>deff(*args,**kwargs):340
...print(repr(args),repr(kwargs))
>>>f(1)341
>>>f(1,2)342
>>>f(1,a=3,b=4)343
>>>t=1,2344
>>>t345
>>>d=dict(k1=3,k2=4)346
>>>d347
>>>f(*t)348
>>>f(**d)349
>>>f(*t,**d)350
>>>m='onetwo'.split()351
>>>f(1,2,*m)352
>>>locals()353
>>>name='Dad'354
>>>'Hi{name}'.format(**locals())355
>>>deff2(a:'x',b:5,c:None,d:list)>float:356
...pass
>>>f2.__annotations__357

Listsaremutable,stringsarenot
>>>#Firstwith``=``and``+``,thenwith``+=``:358
>>>s1=s2='hello'359
>>>s1=s1+'there'360
>>>s1,s2361
>>>s1=s2='hello'362
>>>s1+='there'363
>>>s1,s2364
>>>m1=m2=[1,2,3]365
>>>m1=m1+[4]366
>>>m1,m2367
>>>m1=m2=[1,2,3]368
>>>m1+=[4]369
>>>m1,m2370
>>>#Why?371
>>>#+=isitsownoperator,notidenticaltofoo=foo+1372
>>>importcodeop,dis373
>>>dis.dis(codeop.compile_command('m=[1,2,3];m+=[4]'))374
>>>dis.dis(codeop.compile_command("s='hello';s+='there'"))375
>>>m=[1,2,3]376
>>>m377
>>>m.__iadd__([4])#notereturnvalue378
>>>m379
>>>s1.__iadd__('there')380

Thedifferenceisbecausestr.__iadd__copies,butlist.__iadd__mutates.
https://docs.python.org/3/reference/datamodel.html#object.__iadd__:
Thesemethodsarecalledtoimplementtheaugmentedarithmeticassignments(+=,etc.).Thesemethodsshouldattempttodo
theoperationinplace(modifyingself)andreturntheresult(whichcouldbe,butdoesnothavetobe,self).Ifaspecificmethod
isnotdefined,theaugmentedassignmentfallsbacktothenormalmethods.

>>>t1=(1,2)381
>>>t1[0]+=1382
>>>t2[0]=1+1383
>>>t2=(['one'],)384
>>>t2385
>>>t2[0]+=['two']386
>>>t2387
>>>t2=(['one'],)388
>>>t2389
>>>result=t2[0].__iadd__(['two'])390
>>>result391
>>>t2[0]392
>>>t2[0]=result393
>>>t2394

Parametersbyreference
>>>deftest1(s):395
...print('Before:',s)
...s+='there'
...print('After:',s)
>>>str2='hello'396
>>>str2397
>>>test1(str2)398
>>>str2399
>>>test1('hello')400
>>>deftest2(m):401
...print('Before:',m)
...m+=[4]
...print('After:',m)
>>>list3=[1,2,3]402
>>>list3403
>>>test2(list3)404
>>>list3405

Decorators
Adecoratormodifiesanexistingfunction:
Beforeitstartsexecuting
Includingchangingparameters
Afterit'sdoneexecuting
Includingchangingwhatisreturned
>>>defsquare(n):406
...returnn*n
>>>square(2)407
>>>square(3)408
>>>deftrace_function(f):409
...defnew_f(*args):
...print(
...'called{}({!r})'
....format(f.__qualname__,*args))
...result=f(*args)
...print('returning',result)
...returnresult
...returnnew_f
>>>traced_square=trace_function(square)410
>>>traced_square(2)411
>>>traced_square(3)412

>>>@trace_function413
>>>defcube(n):414
...returnn**3
>>>cube(2)415
>>>cube(3)416
>>>defmemoize(f):417
...cache={}
...defmemoized_f(*args):
...ifargsincache:
...print('Hit!')
...returncache[args]
...ifargsnotincache:
...result=f(*args)
...cache[args]=result
...returnresult
...returnmemoized_f
>>>@memoize418
>>>defcube(n):419
...returnn**3
>>>cube(2)420
>>>cube(3)421
>>>cube(2)422

Exercises:Decorators
Adecoratorisafunctionthattakesafunctionasaparameterandtypicallyreturnsanewfunction,butitcanreturnanything.
Thefollowingcodemisusesdecoratorstomakeyouthinkabouttheirmechanics,whicharereallyquitesimple.Whatdoesit
do?
>>>delx423
>>>x424
>>>defreturn_3(f):425
...return3
>>>@return_3426
>>>defx():427
...pass
>>>x428
>>>type(x)429

Here'sequivalentcodewithoutusing@decoratorsyntax:
>>>#Withoutdecoratorsyntax430
>>>delx431
>>>x432
>>>defx():433
...pass
>>>x434
>>>x=return_3(x)435
>>>x436

Anotherdecorator:
>>>defdoubler(f):437
...defnew_f(*args):
...return2*f(*args)
...returnnew_f
>>>@doubler438
>>>defcube(n):439
...returnn**3
>>>cube(1)440
>>>cube(2)441

>>>@doubler442
>>>defcreate_list(a,b):443
...return[a,b]
>>>create_list(1,2)444
>>>classCounter:445
...def__init__(self):
...self.count=0
...def__call__(self,*args):
...self.count+=1
>>>c=Counter()446
>>>c.count447
>>>c()448
>>>c()449
>>>c.count450
>>>classfunction_counter:451
...def__init__(self,f):
...print('function_counter.__init__called')
...self.f=f
...self.count=0
...def__call__(self,*args):
...print('function_counter.__call__called')
...self.count+=1
...returnself.f(*args)
>>>defplural(s):452
...returns+'s'
>>>plural_counter=function_counter(plural)453
>>>plural_counter('dog')454
>>>plural_counter('cat')455
>>>plural_counter.count456
>>>@function_counter457
>>>defplural(s):458
...returns+'s'
>>>plural('dog')459
>>>plural.count460

Theclassstatement
1.Theclassstatement,whichstartsablockofcode,createsanewnamespaceandallthenamechangesintheblock,i.e.
assignmentandfunctiondefinitions,aremadeinthatnewnamespace.Italsocreatesanamefortheclassinthenamespaceof
themodulewhereitappears.
2.Instancesofaclassarecreatedbycallingtheclass:ClassName()orClassName(parameters).
ClassName.__init__(<newobject>,...)iscalledautomatically,passingasfirstparameteranobject,thenewinstanceofthe
class,whichwascreatedbyacallto__new__().

3.Accessinganattributemethod_nameonaclassinstancereturnsamethodobject,ifmethod_namereferencesamethod(in
ClassNameoritssuperclasses).Amethodobjectbindstheclassinstanceasthefirstparametertothemethod.
Numberclass:
classNumber():
def__init__(self,amount):
self.amount=amount
defadd(self,value):
print('Call:add({!r},{})'
.format(self,value))
returnself.amount+value
>>>classNumber():461

..."""Anumberclass."""
...#ThiscommentsatisfiestheREPL
...__version__='1.0'
...#
...def__init__(self,amount):
...self.amount=amount
...#
...defadd(self,value):
..."""Addavaluetothenumber."""
...print('Call:add({!r},{})'.format(self,value))
...returnself.amount+value
>>>Number462
>>>Number.__version__463
>>>Number.__doc__464
>>>help(Number)465
>>>Number.__init__466
>>>Number.add467
>>>dir(Number)468
>>>defdirp(obj):469
...return[nfornindir(obj)ifnotn.startswith('__')]
>>>dirp(Number)470
>>>number2=Number(2)471
>>>number2.amount472
>>>number2473
>>>number2.__init__474
>>>number2.add475
>>>dirp(number2)476
>>>set(dir(number2))set(dir(Number))477
>>>set(dir(Number))set(dir(number2))478
>>>number2.__dict__479
>>>Number.__dict__480
>>>number2.add481
>>>number2.add(3)482
>>>Number.add483
>>>#Warningunusualcodeahead484
>>>Number.add(2)485
>>>Number.add(2,3)486
>>>Number.add(number2,3)487
>>>number2.add(3)488
>>>#Warningweirdcodeahead489
>>>defset_double_amount(number,amount):490
...number.amount=2*amount
>>>Number.__init__491
>>>help(Number.__init__)492
>>>Number.__init__=set_double_amount493
>>>Number.__init__494
>>>help(Number.__init__)495
>>>number4=Number(2)496
>>>number4.amount497
>>>number4.add(5)498
>>>number4.__init__499
>>>number2.__init__500
>>>defmultiply_by(number,value):501
...returnnumber.amount*value
>>>#Iintentionallymakeamistake...502
>>>number4.mul=multiply_by503
>>>number4.mul504
>>>number4.mul(5)505
>>>number4.mul(number4,5)506
>>>#Where'sthemistake?507
>>>number10=Number(5)508
>>>number10.mul509
>>>dirp(number10)510
>>>dirp(Number)511
>>>dirp(number4)512

>>>Number.mul=multiply_by513
>>>number10.mul(5)514
>>>number4.mul(5)515
>>>dirp(number4)516
>>>number4.__dict__517
>>>number4.mul518
>>>delnumber4.mul519
>>>dirp(number4)520
>>>number4.mul521
>>>Number.mul522
>>>number4.mul(5)523
>>>#Behindthecurtain524
>>>Number525
>>>number4526
>>>Number.add527
>>>number4.add528
>>>dirp(number4.add)529
>>>set(dir(number4.add))set(dir(Number.add))530
>>>number4.add.__self__531
>>>number4.add.__self__isnumber4532
>>>add_value_to_number_4=number4.add533
>>>add_value_to_number_4(6)534
>>>number4.add.__func__535
>>>number4.add.__self__536
>>>number4.add.__func__isNumber.add537
>>>number4.add.__func__isnumber10.add.__func__538
>>>number4.add(5)539
>>>number4.add.__func__(number4.add.__self__,5)540

Thetypefunctionforclasses
"Theclassstatementisjustawaytocallafunction,taketheresult,andputitintoanamespace."GlyphLefkowitzinTurtles
AllTheWayDown...atPyCon2010
type(name,bases,dict)isthefunctionthatgetscalledwhenaclassstatementisusedtocreateaclass.
>>>print(type.__doc__)541
>>>#Let'susethetypefunctiontobuildaclass:542
>>>def_init(self,amount):543
...self.amount=amount
>>>def_add(self,value):544
...returnself.amount+value
>>>Number=type(545
...'Number',
...(object,),
...{'__init__':_init,
...'add':_add,
...})
>>>number3=Number(3)546
>>>type(number3)547
>>>number3.__class__548
>>>number3.__dict__549
>>>number3.amount550
>>>number3.add(4)551
>>>#The*right*way:552
>>>classNumber:553
...def__init__(self,amount):
...self.amount=amount
...#
...defadd(self,value):
...returnself.amount+value

>>>number2=Number(2)554
>>>number2.amount555
>>>number2.add(3)556

Bydefault,classesareconstructedusingtype().Theclassbodyisexecutedinanewnamespaceandtheclassnameisbound
locallytotheresultoftype(name,bases,namespace).
Theclasscreationprocesscanbecustomisedbypassingthemetaclasskeywordargumentintheclassdefinitionline,orby
inheritingfromanexistingclassthatincludedsuchanargument.
https://docs.python.org/3.4/reference/datamodel.html#customizingclasscreation
>>>classNumber(metaclass=type):#defaultmetaclassistype557
...def__init__(self,amount):
...self.amount=amount

Exercises:Theclassstatement
Whatdoesthefollowingcodedo?Notethatreturn_5ignoresitsarguments.
>>>defreturn_5(name,bases,namespace):558
...return5
...
...return_5(None,None,None)
>>>x=return_5(None,None,None)559
>>>x560
>>>type(x)561
>>>dir()562
>>>classy(metaclass=return_5):563
...pass
>>>dir()564
>>>y565
>>>type(y)566

Wesawhowdecoratorsareappliedtofunctions.Theycanalsobeappliedtoclasses.Whatdoesthefollowingcodedo?
>>>#Applyadecoratortoaclass567
>>>defreturn_6(klass):568
...return6
>>>return_6(None)569
>>>dir()570
>>>@return_6571
>>>classz:572
...pass
>>>dir()573
>>>z574
>>>type(z)575

Classdecoratorexample
>>>defclass_counter(klass):576
..."""Modifyklasstocountclassinstantiations"""
...klass.count=0
...klass.__init_orig__=klass.__init__
...defnew_init(self,*args,**kwargs):
...klass.count+=1
...klass.__init_orig__(self,*args,**kwargs)
...klass.__init__=new_init
...returnklass
>>>@class_counter577
>>>classTC:578

...pass
...
...TC.count
...TC()
...TC()
...TC.count

Standardclassmethods
__new__,__init__,__del__,__repr__,__str__,__format__
__getattr__,__getattribute__,__setattr__,__delattr__,__call__,__dir__
__len__,__getitem__,__missing__,__setitem__,__delitem__,__contains__,__iter__,__next__
__lt__,__le__,__gt__,__ge__,__eq__,__ne__,__cmp__,__nonzero__,__hash__
__add__,__sub__,__mul__,__div__,__floordiv__,__mod__,__divmod__,__pow__,__and__,__xor__,__or__,__lshift__,
__rshift__,__neg__,__pos__,__abs__,__invert__,__iadd__,__isub__,__imul__,__idiv__,__itruediv__,
__ifloordiv__,__imod__,__ipow__,__iand__,__ixor__,__ior__,__ilshift__,__irshift__
__int__,__long__,__float__,__complex__,__oct__,__hex__,__coerce__
__radd__,__rsub__,__rmul__,__rdiv__,etc.
__enter__,__exit__
>>>classUpperAttr:579
..."""
...Aclassthatreturnsuppercasevalues
...onuppercaseattributeaccess.
..."""
...def__getattr__(self,name):
...ifname.isupper():
...ifname.lower()inself.__dict__:
...returnself.__dict__[
...name.lower()].upper()
...raiseAttributeError(
..."'{}'objecthasnoattribute{}."
....format(self,name))
>>>d=UpperAttr()580
>>>d.__dict__581
>>>d.foo='bar'582
>>>d.foo583
>>>d.__dict__584
>>>d.FOO585
>>>d.baz586

OptionalExercises:Standardclassmethods
Trythefollowing(inafileifthat'seasier):
>>>classGet:587
...def__getitem__(self,key):
...print('called__getitem__({}{})'
....format(type(key),repr(key)))
>>>g=Get()588
>>>g[1]589
>>>g[1]590
>>>g[0:3]591
>>>g[0:10:2]592
>>>g['Jan']593
>>>g[g]594
>>>m=list('abcdefghij')595
>>>m[0]596
>>>m[1]597
>>>m[::2]598
>>>s=slice(3)599
>>>m[s]600
>>>m[slice(1,3)]601
>>>m[slice(0,2)]602

>>>m[slice(0,len(m),2)]603
>>>m[::2]604

Properties
>>>classPropertyExample:605
...def__init__(self):
...self._x=None
...defgetx(self):
...print('calledgetx()')
...returnself._x
...defsetx(self,value):
...print('calledsetx()')
...self._x=value
...defdelx(self):
...print('delx')
...delself._x
...x=property(getx,setx,delx,"The'x'property.")
>>>p=PropertyExample()606
>>>p.setx('foo')607
>>>p.getx()608
>>>p.x='bar'609
>>>p.x610
>>>delp.x611

Iterators
Aforloopevaluatesanexpressiontogetaniterableandthencallsiter()togetaniterator.
Theiterator's__next__()methodiscalledrepeatedlyuntilStopIterationisraised.
iter(foo)

checksforfoo.__iter__()andcallsitifitexists
elsechecksforfoo.__getitem__()andreturnsanobjectwhichcallsitstartingatzeroandhandlesIndexErrorby
raisingStopIteration.
>>>classMyList:612
...def__init__(self,sequence):
...self.items=sequence
...#
...def__getitem__(self,key):
...print('called__getitem__({})'
....format(key))
...returnself.items[key]
>>>m=MyList(['a','b','c'])613
>>>m.__getitem__(0)614
>>>m.__getitem__(1)615
>>>m.__getitem__(2)616
>>>m.__getitem__(3)617
>>>m[0]618
>>>m[1]619
>>>m[2]620
>>>m[3]621
>>>hasattr(m,'__iter__')622
>>>hasattr(m,'__getitem__')623
>>>it=iter(m)624
>>>it.__next__()625
>>>it.__next__()626
>>>it.__next__()627
>>>it.__next__()628
>>>list(m)629
>>>foriteminm:630

...print(item)

OptionalIterators
>>>m=[1,2,3]631
>>>reversed(m)632
>>>it=reversed(m)633
>>>type(it)634
>>>dir(it)635
>>>it.__next__()636
>>>it.__next__()637
>>>it.__next__()638
>>>it.__next__()639
>>>it.__next__()640
>>>it.__next__()641
>>>m642
>>>foriinm:643
...print(i)
>>>m.__getitem__(0)644
>>>m.__getitem__(1)645
>>>m.__getitem__(2)646
>>>m.__getitem__(3)647
>>>it=reversed(m)648
>>>it2=it.__iter__()649
>>>hasattr(it2,'__next__')650
>>>m=[2*iforiinrange(3)]651
>>>m652
>>>type(m)653
>>>mi=(2*iforiinrange(3))654
>>>mi655
>>>type(mi)656
>>>hasattr(mi,'__next__')657
>>>dir(mi)658
>>>help(mi)659
>>>mi.__next__()660
>>>mi.__next__()661
>>>mi.__next__()662
>>>mi.__next__()663

OptionalExercises:Iterators
>>>m=[1,2,3]664
>>>it=iter(m)665
>>>it.__next__()666
>>>it.__next__()667
>>>it.__next__()668
>>>it.__next__()669
>>>forninm:670
...print(n)
>>>d={'one':1,'two':2,'three':3}671
>>>it=iter(d)672
>>>list(it)673
>>>mi=(2*iforiinrange(3))674
>>>list(mi)675
>>>list(mi)676
>>>importitertools677

Takealookattheitertoolsmoduledocumentation.
>>>m=[1,2,3]678

>>>it1=iter(m)679
>>>it2=iter(it1)680
>>>list(it1)681
>>>list(it2)682
>>>it1=iter(m)683
>>>it2=iter(m)684
>>>list(it1)685
>>>list(it2)686
>>>list(it1)687
>>>list(it2)688

Generators
>>>list_comprehension=[2*iforiinrange(5)]689
>>>list_comprehension690
>>>gen_exp=(2*iforiinrange(5))691
>>>gen_exp692
>>>hasattr(gen_exp,'__next__')693
>>>list(gen_exp)694
>>>list(gen_exp)695
>>>foriin(2*iforiinrange(5)):696
...print(i)
>>>deflist123():697
...yield1
...yield2
...yield3
>>>list123698
>>>list123()699
>>>it=list123()700
>>>it.__next__()701
>>>it.__next__()702
>>>it.__next__()703
>>>it.__next__()704
>>>foriinlist123():705
...print(i)
>>>defeven(limit):706
...foriinrange(0,limit,2):
...print('Yielding',i)
...yieldi
...print('doneloop,fallingout')
>>>it=even(3)707
>>>it708
>>>it.__next__()709
>>>it.__next__()710
>>>it.__next__()711
>>>foriineven(3):712
...print(i)
>>>list(even(10))713

Comparetheseversions
>>>defeven_1(limit):714
...foriinrange(0,limit,2):
...yieldi
>>>defeven_2(limit):715
...result=[]
...foriinrange(0,limit,2):
...result.append(i)
...returnresult
>>>[iforiineven_1(10)]716
>>>[iforiineven_2(10)]717

>>>defparagraphs(lines):718
...result=''
...forlineinlines:
...ifline.strip()=='':
...yieldresult
...result=''
...else:
...result+=line
...yieldresult
>>>list(paragraphs(open('eg.txt')))719
>>>len(list(paragraphs(open('eg.txt'))))720

Firstclassobjects
Pythonexposesmanylanguagefeaturesandplacesalmostnoconstraintsonwhattypesdatastructurescanhold.
Here'sanexampleofusingadictionaryoffunctionstocreateasimplecalculator.Insomelanguagesthisrequireacaseor
switchstatement,oraseriesofifstatements.Ifyou'vebeenusingsuchalanguageforawhile,thisexamplemayhelpyou
expandtherangeofsolutionsyoucanimagineinPython.
>>>7+3721
>>>importoperator722
>>>operator.add(7,3)723
>>>expr='7+3'724
>>>lhs,op,rhs=expr725
>>>lhs,op,rhs726
>>>lhs,rhs=int(lhs),int(rhs)727
>>>lhs,op,rhs728
>>>op,lhs,rhs729
>>>operator.add(lhs,rhs)730
>>>ops={731
...'+':operator.add,
...'':operator.sub,
...}
>>>ops[op](lhs,rhs)732
>>>defcalc(expr):733
...lhs,op,rhs=expr
...lhs,rhs=int(lhs),int(rhs)
...returnops[op](lhs,rhs)
>>>calc('7+3')734
>>>calc('95')735
>>>calc('8/2')736
>>>ops['/']=operator.truediv737
>>>calc('8/2')738
>>>classUnpacker:739
...slices={
...'first':slice(0,3),
...'hyde':slice(9,12),
...'myname':slice(18,21)
...}
...#
...def__init__(self,record):
...self.record=record
...#
...def__getattr__(self,attr):
...ifattrinself.slices:
...returnself.record[self.slices[attr]]
...raiseAttributeError(
..."'Unpacker'objecthasnoattribute'{}'"
....format(attr))
...
>>>u=Unpacker('abcdefghijklmnopqrstuvwxyz')740

>>>u.first741
>>>u.hyde742
>>>u.myname743

Optional:Closuresandpartialfunctions
>>>deflog(message,subsystem):744
..."""
...Writethecontentsof'message'
...tothespecifiedsubsystem.
..."""
...print('LOG{}:{}'.format(subsystem,message))
>>>log('Initializingserver','server')745
>>>log('Readingconfigfile','server')746
>>>defserver_log(message):747
...log(message,'server')
>>>server_log('Initializingserver')748
>>>server_log('Readingconfigfile')749
>>>importfunctools750
>>>server_log=functools.partial(log,subsystem='server')751
>>>server_log752
>>>server_log.funcislog753
>>>server_log.keywords754
>>>server_log('Initializingserver')755
>>>server_log('Readingconfigfile')756

Boundmethodsareaformofpartials:
>>>SENTENCE_ENDING='.?!'757
>>>sentence='Thisisasentence!'758
>>>sentence[1]inSENTENCE_ENDING759
>>>'.'inSENTENCE_ENDING760
>>>SENTENCE_ENDING.__contains__('.')761
>>>SENTENCE_ENDING.__contains__(',')762
>>>is_sentence_ending=SENTENCE_ENDING.__contains__763
>>>is_sentence_ending('.')764
>>>is_sentence_ending(',')765

Yetanotherwaytobindsomedataistocreateaclassandgiveita__call__method:
>>>classSentenceEnding:766
...def__init__(self,characters):
...self.punctuation=characters
...#
...def__call__(self,sentence):
...returnsentence[1]inself.punctuation
>>>is_sentence1=SentenceEnding('.')767
>>>is_sentence1('Thisisatest.')768
>>>is_sentence1('Thisisatest!')769
>>>is_sentence2=SentenceEnding('.!?')770
>>>is_sentence2('Thisisatest.')771
>>>is_sentence2('Thisisatest!')772

OptionalExercises:namedtuple,operator
>>>importcollections773
>>>Month=collections.namedtuple(774
...'Month','namenumberdays',verbose=True)
>>>jan=Month('January',1,31)775

>>>jan.name776
>>>jan[0]777
>>>apr=Month('April',3,30)778
>>>apr.days779
>>>apr[2]780
>>>jul=Month('July',7,31)781
>>>m=[jan,apr,jul]782
>>>defmonth_days(month):783
...returnmonth.days
>>>importoperator784
>>>sorted(m,key=operator.itemgetter(0))785
>>>sorted(m,key=operator.attrgetter('name'))786
>>>sorted(m,key=operator.attrgetter('number'))787

Evaluations

You might also like