+ All Categories
Home > Documents > Reuven M. Lerner • PyCon 2020 [email protected] ... · Function dissection lab Reuven M. Lerner...

Reuven M. Lerner • PyCon 2020 [email protected] ... · Function dissection lab Reuven M. Lerner...

Date post: 25-Aug-2020
Category:
Upload: others
View: 4 times
Download: 0 times
Share this document with a friend
56
Function dissection lab Reuven M. Lerner • PyCon 2020 [email protected] @reuvenmlerner 1
Transcript
Page 1: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Function dissection labReuven M. Lerner • PyCon 2020

[email protected] • @reuvenmlerner

1

Page 2: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• Corporate training

• Video courses about Python + Git

• Weekly Python Exercise

• More info at https://lerner.co.il/

• “Python Workout” — published by Manning

• “Better developers” — free, weekly newsletter about Python

• https://BetterDevelopersWeekly.com/

I teach Python

2

2

Page 3: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

(And then we’ll take it apart…)

3

Let’s write some code!

3

Page 4: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

x = [10, 20, 30]d = {'a': 1, 'b': 2, 'c': 3}

def hello(name): return f'Hello, {name}!'

Consider this code

4

4

Page 5: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab 5

5

Page 6: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• When you use “def”, you create a function object…

• …and then you assign it to a variable

• Functions are nouns, not just verbs!

• Functions (like all objects) can be assigned hello2 = hello

• Functions (like all objects) can be passed as arguments hello(hello)

• Functions (like all objects) have attributes dir(hello)

Function objects?

6

6

Page 7: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

How does Python use a function’s attributes?

7

7

Page 8: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

>>> def hello(name): return f'Hello, {name}!'

>>> hello('world')'Hello, world!'

>>> hello()TypeError: hello() missing 1 required positional argument: 'name'

Example

8

8

Page 9: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

How did Python know?

9

9

Page 10: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• The most important attribute in a function is __code__.

• Its attributes contain:

• Python byte code

• Hints to the Python interpreter about our function

__code__

10

10

Page 11: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• How many arguments does the function take?

>>> hello.__code__.co_argcount

1

__code__.co_argcount

11

11

Page 12: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

>>> def hello(name): return f'Hello, {name}!'

>>> hello('world')'Hello, world!'

• Python says:

• co_argcount say that we need 1 argument

• The user passed 1 argument

So when we run this:

12

12

Page 13: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

>>> def hello(name): return f'Hello, {name}!'

>>> hello()TypeError: hello() missing 1 required positional argument: 'name'

• Python says:

• co_argcount say that we need 1 argument

• We didn’t pass any arguments — error!

But when we do this:

13

13

Page 14: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• A tuple of strings

• Lists all of a function’s local variables

>>> hello.__code__.co_varnames

('name',)

• So Python knows:

• The function requires one argument

• That argument will be assigned to “name”

• If we get no arguments, then “name” is missing a value

__code__.co_varnames

14

14

Page 15: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

>>> def hello(name): return f'Hello, {name}!'

>>> hello()TypeError: hello() missing 1 required positional argument: ‘name'

Sure enough:

15

15

Page 16: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

>>> def hello(first, last): return f'Hello, {first} {last}!'

>>> hello.__code__.co_argcount2

>>> hello.__code__.co_varnames('first', ‘last')

Two parameters

16

16

Page 17: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

>>> hello('Reuven')

TypeError: hello() missing 1 required positional argument: 'last'

>>> hello(last='Lerner')

TypeError: hello() missing 1 required positional argument: ‘first'

>>> hello('a', 'b', 'c')

TypeError: hello() takes 2 positional arguments but 3 were given

Error messages use this info

17

17

Page 18: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• What if we define a local variable, as well?

>>> def hello(first, last): s = f'Hello, {first} {last}!' return s

>>> hello.__code__.co_argcount2

>>> hello.__code__.co_varnames('first', 'last', 's')

• The first co_argcount elements of co_varnames are parameters

Additional variables

18

18

Page 19: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

>>> def hello(first, last, *args):

return f'Hello {first} {last}, args = {args}'

>>> hello('a', 'b', 'c', 'd', 'e')

"Hello a b, args = ('c', 'd', 'e')"

>>> hello.__code__.co_argcount

2

>>> hello.__code__.co_varnames

('first', 'last', 'args')

*args

19

19

Page 20: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• This information is kept in co_flags, a int

• This int is the bitwise “and” of several bit flags

• If the bit is 1, then the flag is “on.” Otherwise, it’s off.

So, how does Python know?

20

25 Generator

24 Nested

23 **kwargs

22 *args

21 New locals

20 Optimized

Always on

20

Page 21: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

co_optimized 0x01 # use fast locals

co_newlocals 0x02 # new dict for code block

co_varargs 0x04 # function has *args

co_varkeywords 0x08 # function has **kwargs

co_nested 0x10 # nested scopes

co_generator 0x20 # it’s a generator function

It’s easier in hex, you know

21

21

Page 22: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

>>> hello.__code__.co_flags & 0x04 # Yes *args

4

>>> hello.__code__.co_flags & 0x08 # No **kwargs

0

Sure enough…

22

22

Page 23: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

>>> def hello(**kwargs):

return f'Hello, {kwargs}!'

>>> hello.__code__.co_flags & 0x04 # No *args

0

>>> hello.__code__.co_flags & 0x08 # Yes **kwargs

8

Same goes for **kwargs

23

23

Page 24: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

>>> dis.show_code(hello)

Name: hello

Filename: <ipython-input-56-823d1147b5b6>

Argument count: 0

Kw-only arguments: 0

Number of locals: 1

Stack size: 3

Flags: OPTIMIZED, NEWLOCALS, VARKEYWORDS, NOFREE

Constants:

0: None

1: 'Hello, '

2: '!'

Variable names:

0: kwargs

dis.show_code

24

24

Page 25: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• Literal values are stored in __code__.co_consts

• The first (zero-index) item in co_consts is always None

• Other constants (e.g., ints and strings) are also stored

• Notice that f-strings are broken up into parts!

• The byte codes then refer to constants by index number

>>> hello.__code__.co_consts

(None, 'Hello, ', '!')

Constants?

25

25

Page 26: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• The function’s byte codes are stored in co_code, as a bytestring:

>>> hello.__code__.co_code

b'd\x01|\x00\x9b\x00d\x02\x9d\x03S\x00'

• It’s probably easier to understand with “dis.dis”:

>>> dis.dis(hello)

2 0 LOAD_CONST 1 ('Hello, ')

2 LOAD_FAST 0 (kwargs)

4 FORMAT_VALUE 0

6 LOAD_CONST 2 ('!')

8 BUILD_STRING 3

10 RETURN_VALUE

Bytecodes

26

26

Page 27: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

def hello(name='world'): return f'Hello, {name}'

>>> hello.__code__.co_argcount

1

• It seems like our function works just like before

• But we know that we can call it with no arguments

• How does this work?

What about defaults?

27

27

Page 28: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• A function’s defaults are stored in __defaults__

• (Note: This is a function attribute, not a __code__ attribute!)

>>> hello.__defaults__

('world',)

• __defaults__ is always a tuple

• No defaults? Then it’s an empty tuple

__defaults__

28

28

Page 29: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• It compares the arguments with co_argcount

• Does the number match?

• Pass arguments and call the function

• Not enough arguments?

• Checks if __defaults__ can close the gap

• If so, use enough from __defaults__ to get to co_argcount

• Too many arguments?

• Check co_flags to see if *args is defined

• If so, assign remaining arguments to *args

• Or whatever variable is named in co_varnames[co_argcount]

When Python calls a function…

29

29

Page 30: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

def add_one(x): x.append(1)

mylist = [10, 20, 30]

add_one(mylist) print(mylist) [10, 20, 30, 1]

add_one(mylist) print(mylist) [10, 20, 30, 1, 1]

Consider this function:

30

30

Page 31: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

def add_one(x=[]): x.append(1) return x

print(add_one()) print(add_one()) print(add_one())

Let’s add a default

31

[1]

[1, 1]

[1, 1, 1]

31

Page 32: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• __defaults__ is populated when you define the function

def add_one(x=[]): x.append(1) return x

• Conclusion: Never use mutable defaults!

The problem?

32

32

Page 33: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

$ pylint add_one.py

************* Module add_one

add_one.py:4:0: W0102: Dangerous default value [] as argument (dangerous-default-value)

Don’t ignore this warning!

33

33

Page 34: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

def hello(*args, sep=' '): return f'Hello, {sep.join(args)}!'

>>> hello('a', 'b', 'c') 'Hello, a b c!'

>>> hello('a', 'b', 'c', sep='*') 'Hello, a*b*c!'

Keyword-only arguments

34

34

Page 35: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• It isn’t counted with the other arguments:

>>> hello.__code__.co_argcount

0

• Rather, it’s listed here:

>>> hello.__code__.co_kwonlyargcount

1

Where does Python put that?

35

35

Page 36: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• co_argcount — number of mandatory, positional arguments

• __defaults__ — values that make co_argcount flexible

• co_flags

• Do we assign extra positional args to *args?

• Do we assign extra keyword args to **kwargs?

• co_kwonlyargcount — number of keyword-only args

Python checks in many places!

36

36

Page 37: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

x = 100

def func(): print(f'In func, x = {x}')

print(f'Before, x = {x}') func() print(f'After, x = {x}')

Let's talk about scoping

37

# 100# 100# 100

• L — Local

• E — Enclosing

• G — Global

• B — Builtins

37

Page 38: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

def func(): print(f'In func, x = {x}’)

• It checks in the attributes, of course:

>>> func.__code__.co_varnames

()

• Since “x” isn’t in co_varnames, it isn’t a local variable.

How does Python know x isn’t local?

38

38

Page 39: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

x = 100

def func(): x = 200 print(f'In func, x = {x}')

print(f'Before, x = {x}') func() print(f'After, x = {x}')

Let's make things more complex

39

# 100# 200# 100

• L — Local

• E — Enclosing

• G — Global

• B — Builtins

39

Page 40: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• Because it’s in co_varnames

>>> func.__code__.co_varnames

('x',)

• Notice: co_varnames is populated at compile time, not runtime!

How does Python know x is local?

40

40

Page 41: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

x = 100

def func(): print(f'In func, x = {x}’) x = 200

print(f'Before, x = {x}') func() print(f'After, x = {x}')

Let’s make a slight change…

41

Swapped

these lines

41

Page 42: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

Before, x = 100

Traceback (most recent call last):

File "./func12.py", line 12, in <module>

func()

File "./func12.py", line 7, in func

print(f'In func, x = {x}')

UnboundLocalError: local variable 'x' referenced before assignment

What happens now?

42

42

Page 43: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• Consider our function:

def func(): print(f'In func, x = {x}') x = 200

• Because we assign to x in the function, x is local

• x is thus in __code__.co_varnames

• When we run the function, we need x’s value for the “print”

• Python knows that x is local, but has no local value…

Huh?

43

43

Page 44: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

UnboundLocalError

44

44

Page 45: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

x = 100

def func(): x += 1 print(f'In func, x = {x}’)

print(f'Before, x = {x}') func() print(f'After, x = {x}')

A more common version of this problem

45

Same as

x = x + 1

45

Page 46: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

The “global” declaration

46

x = 100

def func(): global x x = 200 print(f'In func, x = {x}')

print(f'Before, x = {x}') func() print(f'After, x = {x}')

# 100# 200# 200

46

Page 47: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• It removes a variable from co_varnames:

>>> func.__code__.co_varnames

()

• Python uses LEGB to look for “x”

• It cannot find “x” in the tuple of local variable names

• So it assigns to the global variable x!

What does “global” do?

47

47

Page 48: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

>>> dis.dis(func)

3 0 LOAD_CONST 1 (200)

2 STORE_FAST 0 (x)

4 4 LOAD_GLOBAL 0 (print)

6 LOAD_CONST 2 ('In func, x = ')

8 LOAD_FAST 0 (x)

10 FORMAT_VALUE 0

12 BUILD_STRING 2

14 CALL_FUNCTION 1

16 POP_TOP

18 LOAD_CONST 0 (None)

20 RETURN_VALUE

Bytecodes with a local “x”

48

48

Page 49: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

>>> dis.dis(func)

5 0 LOAD_CONST 1 (200)

2 STORE_GLOBAL 0 (x)

6 4 LOAD_GLOBAL 1 (print)

6 LOAD_CONST 2 ('In func, x = ')

8 LOAD_GLOBAL 0 (x)

10 FORMAT_VALUE 0

12 BUILD_STRING 2

14 CALL_FUNCTION 1

16 POP_TOP

18 LOAD_CONST 0 (None)

20 RETURN_VALUE

Bytecodes with a global “x”

49

49

Page 50: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

def outer(): run_counter = 0 total = 0

def inner(x): run_counter += 1 total += x print(f'Run {run_counter}, total is {total}')

return inner

func = outer() for i in range(10, 100, 10): func(i)

Putting the “E” in LEGB

50

50

Page 51: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

UnboundLocalError

51

51

Page 52: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

def outer(): run_counter = 0 total = 0

def inner(x): nonlocal run_counter, total run_counter += 1 total += x print(f'Run {run_counter}, total is {total}')

return inner

func = outer() for i in range(10, 100, 10): func(i)

Make them … nonlocal

52

52

Page 53: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab 53

53

Page 54: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

>>> func = outer()

>>> func.__code__.co_freevars

('run_counter', ‘total')

• But that’s not all!

• “outer” knows which of its local variables are referenced:

>>> outer.__code__.co_cellvars

('run_counter', 'total')

How?

54

54

Page 55: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• “def” does two things

• Creates a function object

• Assigns that function object to a variable

• Function objects contain attributes

• Byte codes

• Hints to Python for running the function

• Attributes dictate behavior we often take for granted

• Argument assignment

• Scoping

What have we learned?

55

55

Page 56: Reuven M. Lerner • PyCon 2020 reuven@lerner.co.il ... · Function dissection lab Reuven M. Lerner • @reuvenmlerner • • Literal values are stored in __code__.co_consts •

Reuven M. Lerner • @reuvenmlerner • https://lerner.co.ilFunction dissection lab

• E-mail me: [email protected]

• Follow me on Twitter: @reuvenmlerner

• See you in Pittsburgh in 2021!

Questions or comments?

56

56


Recommended