Online Book Reader

Home Category

Learning Python - Mark Lutz [556]

By Root 1930 0
1.0)) # percent passed by name or position

def giveRaise(self, percent):

self.pay = int(self.pay * (1 + percent))

bob = Person('Bob Smith', 'dev', 100000)

sue = Person('Sue Jones', 'dev', 100000)

bob.giveRaise(.10)

sue.giveRaise(percent=.20)

print(bob.pay, sue.pay)

#bob.giveRaise(1.10)

#bob.giveRaise(percent=1.20)

# Test omitted defaults: skipped

@rangetest(a=(1, 10), b=(1, 10), c=(1, 10), d=(1, 10))

def omitargs(a, b=7, c=8, d=9):

print(a, b, c, d)

omitargs(1, 2, 3, 4)

omitargs(1, 2, 3)

omitargs(1, 2, 3, d=4)

omitargs(1, d=4)

omitargs(d=4, a=1)

omitargs(1, b=2, d=4)

omitargs(d=8, c=7, a=1)

#omitargs(1, 2, 3, 11) # Bad d

#omitargs(1, 2, 11) # Bad c

#omitargs(1, 2, 3, d=11) # Bad d

#omitargs(11, d=4) # Bad a

#omitargs(d=4, a=11) # Bad a

#omitargs(1, b=11, d=4) # Bad b

#omitargs(d=8, c=7, a=11) # Bad a

When this script is run, out-of-range arguments raise an exception as before, but arguments may be passed by either name or position, and omitted defaults are not validated. This code runs on both 2.6 and 3.0, but extra tuple parentheses print in 2.6. Trace its output and test this further on your own to experiment; it works as before, but its scope has been broadened:

C:\misc> C:\python30\python devtools_test.py

Bob is 40 years old

Bob is 40 years old

birthday = 5/1/1963

110000 120000

1 2 3 4

Argument "d" defaulted

1 2 3 9

1 2 3 4

Argument "c" defaulted

Argument "b" defaulted

1 7 8 4

Argument "c" defaulted

Argument "b" defaulted

1 7 8 4

Argument "c" defaulted

1 2 8 4

Argument "b" defaulted

1 7 7 8

On validation errors, we get an exception as before (unless the –O command-line argument is passed to Python) when one of the method test lines is uncommented:

TypeError: giveRaise argument "percent" not in 0.0..1.0

Implementation Details

This decorator’s code relies on both introspection APIs and subtle constraints of argument passing. To be fully general we could in principle try to mimic Python’s argument matching logic in its entirety to see which names have been passed in which modes, but that’s far too much complexity for our tool. It would be better if we could somehow match arguments passed by name against the set of all expected arguments’ names, in order to determine which position arguments actually appear in during a given call.

Function introspection

It turns out that the introspection API available on function objects and their associated code objects has exactly the tool we need. This API was briefly introduced in Chapter 19, but we’ll actually put it to use here. The set of expected argument names is simply the first N variable names attached to a function’s code object:

# In Python 3.0 (and 2.6 for compatibility):

>>> def func(a, b, c, d):

... x = 1

... y = 2

...

>>> code = func.__code__ # Code object of function object

>>> code.co_nlocals

6

>>> code.co_varnames # All local var names

('a', 'b', 'c', 'd', 'x', 'y')

>>> code.co_varnames[:code.co_argcount] # First N locals are expected args

('a', 'b', 'c', 'd')

>>> import sys # For backward compatibility

>>> sys.version_info # [0] is major release number

(3, 0, 0, 'final', 0)

>>> code = func.__code__ if sys.version_info[0] == 3 else func.func_code

The same API is available in older Pythons, but the func.__code__ attribute is spelled as func.func_code in 2.5 and earlier (the newer __code__ attribute is also redundantly available in 2.6 for portability). Run a dir call on function and code objects for more details.

Argument assumptions

Given this set of expected argument names, the solution relies on two constraints on argument passing order imposed by Python (these still hold true in both 2.6 and 3.0):

At the call, all positional arguments appear before all keyword arguments.

In the def, all nondefault arguments appear before all default arguments.

That is, a nonkeyword argument cannot generally follow a keyword argument at a call, and a nondefault argument cannot follow a default argument at a definition. All “name=value” syntax must appear after

Return Main Page Previous Page Next Page

®Online Book Reader