Many edits
This commit is contained in:
@@ -1,25 +1,31 @@
|
||||
|
||||
[Contents](../Contents) \| [Previous (6.4 Generator Expressions)](../06_Generators/04_More_generators) \| [Next (7.2 Anonymous Functions)](02_Anonymous_function)
|
||||
|
||||
# 7.1 Variable Arguments
|
||||
|
||||
This section covers variadic function arguments, sometimes described as
|
||||
`*args` and `**kwargs`.
|
||||
|
||||
### Positional variable arguments (*args)
|
||||
|
||||
A function that accepts *any number* of arguments is said to use variable arguments.
|
||||
For example:
|
||||
|
||||
```python
|
||||
def foo(x, *args):
|
||||
def f(x, *args):
|
||||
...
|
||||
```
|
||||
|
||||
Function call.
|
||||
|
||||
```python
|
||||
foo(1,2,3,4,5)
|
||||
f(1,2,3,4,5)
|
||||
```
|
||||
|
||||
The arguments get passed as a tuple.
|
||||
The extra arguments get passed as a tuple.
|
||||
|
||||
```python
|
||||
def foo(x, *args):
|
||||
def f(x, *args):
|
||||
# x -> 1
|
||||
# args -> (2,3,4,5)
|
||||
```
|
||||
@@ -30,37 +36,52 @@ A function can also accept any number of keyword arguments.
|
||||
For example:
|
||||
|
||||
```python
|
||||
def foo(x, y, **kwargs):
|
||||
def f(x, y, **kwargs):
|
||||
...
|
||||
```
|
||||
|
||||
Function call.
|
||||
|
||||
```python
|
||||
foo(2,3,flag=True,mode='fast',header='debug')
|
||||
f(2, 3, flag=True, mode='fast', header='debug')
|
||||
```
|
||||
|
||||
The extra keywords are passed in a dictionary.
|
||||
|
||||
```python
|
||||
def foo(x, y, **kwargs):
|
||||
def f(x, y, **kwargs):
|
||||
# x -> 2
|
||||
# y -> 3
|
||||
# kwargs -> { 'flat': True, 'mode': 'fast', 'header': 'debug' }
|
||||
# kwargs -> { 'flag': True, 'mode': 'fast', 'header': 'debug' }
|
||||
```
|
||||
|
||||
### Combining both
|
||||
|
||||
A function can also combine any number of variable keyword and non-keyword arguments.
|
||||
Function definition.
|
||||
A function can also accept any number of variable keyword and non-keyword arguments.
|
||||
|
||||
```python
|
||||
def foo(*args, **kwargs):
|
||||
def f(*args, **kwargs):
|
||||
...
|
||||
```
|
||||
|
||||
This function takes any combination of positional or keyword arguments.
|
||||
It is sometimes used when writing wrappers or when you want to pass arguments through to another function.
|
||||
Function call.
|
||||
|
||||
```python
|
||||
f(2, 3, flag=True, mode='fast', header='debug')
|
||||
```
|
||||
|
||||
The arguments are separated into positional and keyword components
|
||||
|
||||
```python
|
||||
def f(*args, **kwargs):
|
||||
# args = (2, 3)
|
||||
# kwargs -> { 'flag': True, 'mode': 'fast', 'header': 'debug' }
|
||||
...
|
||||
```
|
||||
|
||||
This function takes any combination of positional or keyword
|
||||
arguments. It is sometimes used when writing wrappers or when you
|
||||
want to pass arguments through to another function.
|
||||
|
||||
### Passing Tuples and Dicts
|
||||
|
||||
@@ -68,7 +89,7 @@ Tuples can be expanded into variable arguments.
|
||||
|
||||
```python
|
||||
numbers = (2,3,4)
|
||||
foo(1, *numbers) # Same as f(1,2,3,4)
|
||||
f(1, *numbers) # Same as f(1,2,3,4)
|
||||
```
|
||||
|
||||
Dictionaries can also be expaded into keyword arguments.
|
||||
@@ -79,12 +100,10 @@ options = {
|
||||
'delimiter' : ',',
|
||||
'width' : 400
|
||||
}
|
||||
foo(data, **options)
|
||||
# Same as foo(data, color='red', delimiter=',', width=400)
|
||||
f(data, **options)
|
||||
# Same as f(data, color='red', delimiter=',', width=400)
|
||||
```
|
||||
|
||||
These are not commonly used except when writing library functions.
|
||||
|
||||
## Exercises
|
||||
|
||||
### Exercise 7.1: A simple example of variable arguments
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
[Contents](../Contents) \| [Previous (7.1 Variable Arguments)](01_Variable_arguments) \| [Next (7.3 Returning Functions)](03_Returning_functions)
|
||||
|
||||
# 7.2 Anonymous Functions and Lambda
|
||||
|
||||
### List Sorting Revisited
|
||||
@@ -30,7 +32,9 @@ It seems simple enough. However, how do we sort a list of dicts?
|
||||
|
||||
By what criteria?
|
||||
|
||||
You can guide the sorting by using a *key function*. The *key function* is a function that receives the dictionary and returns the value in a specific key.
|
||||
You can guide the sorting by using a *key function*. The *key
|
||||
function* is a function that receives the dictionary and returns the
|
||||
value of interest for sorting.
|
||||
|
||||
```python
|
||||
def stock_name(s):
|
||||
@@ -39,7 +43,7 @@ def stock_name(s):
|
||||
portfolio.sort(key=stock_name)
|
||||
```
|
||||
|
||||
The value returned by the *key function* determines the sorting.
|
||||
Here's the result.
|
||||
|
||||
```python
|
||||
# Check how the dictionaries are sorted by the `name` key
|
||||
@@ -56,13 +60,16 @@ The value returned by the *key function* determines the sorting.
|
||||
|
||||
### Callback Functions
|
||||
|
||||
Callback functions are often short one-line functions that are only used for that one operation. For example of previous sorting example.
|
||||
Programmers often ask for a short-cut, so is there a shorter way to specify custom processing for `sort()`?
|
||||
In the above example, the key function is an example of a callback
|
||||
function. The `sort()` method "calls back" to a function you supply.
|
||||
Callback functions are often short one-line functions that are only
|
||||
used for that one operation. Programmers often ask for a short-cut
|
||||
for specifying this extra processing.
|
||||
|
||||
### Lambda: Anonymous Functions
|
||||
|
||||
Use a lambda instead of creating the function.
|
||||
In our previous sorting example.
|
||||
Use a lambda instead of creating the function. In our previous
|
||||
sorting example.
|
||||
|
||||
```python
|
||||
portfolio.sort(key=lambda s: s['name'])
|
||||
@@ -156,6 +163,6 @@ Try sorting the portfolio according to the price of each stock
|
||||
|
||||
Note: `lambda` is a useful shortcut because it allows you to
|
||||
define a special processing function directly in the call to `sort()` as
|
||||
opposed to having to define a separate function first (as in part a).
|
||||
opposed to having to define a separate function first.
|
||||
|
||||
[Contents](../Contents) \| [Previous (7.1 Variable Arguments)](01_Variable_arguments) \| [Next (7.3 Returning Functions)](03_Returning_functions)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
[Contents](../Contents) \| [Previous (7.2 Anonymous Functions)](02_Anonymous_function) \| [Next (7.4 Decorators)](04_Function_decorators)
|
||||
|
||||
# 7.3 Returning Functions
|
||||
|
||||
This section introduces the idea of closures.
|
||||
This section introduces the idea of using functions to create other functions.
|
||||
|
||||
### Introduction
|
||||
|
||||
@@ -27,7 +29,8 @@ Adding 3 4
|
||||
|
||||
### Local Variables
|
||||
|
||||
Observe how to inner function refers to variables defined by the outer function.
|
||||
Observe how to inner function refers to variables defined by the outer
|
||||
function.
|
||||
|
||||
```python
|
||||
def add(x, y):
|
||||
@@ -38,7 +41,8 @@ def add(x, y):
|
||||
return do_add
|
||||
```
|
||||
|
||||
Further observe that those variables are somehow kept alive after `add()` has finished.
|
||||
Further observe that those variables are somehow kept alive after
|
||||
`add()` has finished.
|
||||
|
||||
```python
|
||||
>>> a = add(3,4)
|
||||
@@ -51,7 +55,7 @@ Adding 3 4 # Where are these values coming from?
|
||||
|
||||
### Closures
|
||||
|
||||
When an inner function is returned as a result, the inner function is known as a *closure*.
|
||||
When an inner function is returned as a result, that inner function is known as a *closure*.
|
||||
|
||||
```python
|
||||
def add(x, y):
|
||||
@@ -62,7 +66,10 @@ def add(x, y):
|
||||
return do_add
|
||||
```
|
||||
|
||||
*Essential feature: A closure retains the values of all variables needed for the function to run properly later on.*
|
||||
*Essential feature: A closure retains the values of all variables
|
||||
needed for the function to run properly later on.* Think of a
|
||||
closure as a function plus an extra environment that holds the values
|
||||
of variables that it depends on.
|
||||
|
||||
### Using Closures
|
||||
|
||||
@@ -99,7 +106,7 @@ Closures carry extra information around.
|
||||
```python
|
||||
def add(x, y):
|
||||
def do_add():
|
||||
print('Adding %s + %s -> %s' % (x, y, x + y))
|
||||
print(f'Adding {x} + {y} -> {x+y}')
|
||||
return do_add
|
||||
|
||||
def after(seconds, func):
|
||||
@@ -110,8 +117,6 @@ after(30, add(2, 3))
|
||||
# `do_add` has the references x -> 2 and y -> 3
|
||||
```
|
||||
|
||||
A function can have its own little environment.
|
||||
|
||||
### Code Repetition
|
||||
|
||||
Closures can also be used as technique for avoiding excessive code repetition.
|
||||
@@ -122,11 +127,12 @@ You can write functions that make code.
|
||||
### Exercise 7.7: Using Closures to Avoid Repetition
|
||||
|
||||
One of the more powerful features of closures is their use in
|
||||
generating repetitive code. If you refer back to exercise 5.2
|
||||
recall the code for defining a property with type checking.
|
||||
generating repetitive code. If you refer back to [Exercise
|
||||
5.7](../05_Object_model/02_Classes_encapsulation), recall the code for
|
||||
defining a property with type checking.
|
||||
|
||||
```python
|
||||
class Stock(object):
|
||||
class Stock:
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
@@ -173,7 +179,7 @@ Now, try it out by defining a class like this:
|
||||
```python
|
||||
from typedproperty import typedproperty
|
||||
|
||||
class Stock(object):
|
||||
class Stock:
|
||||
name = typedproperty('name', str)
|
||||
shares = typedproperty('shares', int)
|
||||
price = typedproperty('price', float)
|
||||
@@ -211,7 +217,7 @@ Float = lambda name: typedproperty(name, float)
|
||||
Now, rewrite the `Stock` class to use these functions instead:
|
||||
|
||||
```python
|
||||
class Stock(object):
|
||||
class Stock:
|
||||
name = String('name')
|
||||
shares = Integer('shares')
|
||||
price = Float('price')
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
[Contents](../Contents) \| [Previous (7.3 Returning Functions)](03_Returning_functions) \| [Next (7.5 Decorated Methods)](05_Decorated_methods)
|
||||
|
||||
# 7.4 Function Decorators
|
||||
|
||||
This section introduces the concept of a decorator. This is an advanced
|
||||
@@ -12,7 +14,7 @@ def add(x, y):
|
||||
return x + y
|
||||
```
|
||||
|
||||
Now, consider the function with some logging.
|
||||
Now, consider the function with some logging added to it.
|
||||
|
||||
```python
|
||||
def add(x, y):
|
||||
@@ -32,13 +34,15 @@ def sub(x, y):
|
||||
|
||||
*Observation: It's kind of repetitive.*
|
||||
|
||||
Writing programs where there is a lot of code replication is often really annoying.
|
||||
They are tedious to write and hard to maintain.
|
||||
Especially if you decide that you want to change how it works (i.e., a different kind of logging perhaps).
|
||||
Writing programs where there is a lot of code replication is often
|
||||
really annoying. They are tedious to write and hard to maintain.
|
||||
Especially if you decide that you want to change how it works (i.e., a
|
||||
different kind of logging perhaps).
|
||||
|
||||
### Example continuation
|
||||
### Code that makes logging
|
||||
|
||||
Perhaps you can make *logging wrappers*.
|
||||
Perhaps you can make a function that makes functions with logging
|
||||
added to them. A wrapper.
|
||||
|
||||
```python
|
||||
def logged(func):
|
||||
@@ -65,7 +69,9 @@ logged_add(3, 4) # You see the logging message appear
|
||||
|
||||
This example illustrates the process of creating a so-called *wrapper function*.
|
||||
|
||||
**A wrapper is a function that wraps another function with some extra bits of processing.**
|
||||
A wrapper is a function that wraps around another function with some
|
||||
extra bits of processing, but otherwise works in the exact same way
|
||||
as the original function.
|
||||
|
||||
```python
|
||||
>>> logged_add(3, 4)
|
||||
@@ -100,6 +106,8 @@ It is said to *decorate* the function.
|
||||
There are many more subtle details to decorators than what has been presented here.
|
||||
For example, using them in classes. Or using multiple decorators with a function.
|
||||
However, the previous example is a good illustration of how their use tends to arise.
|
||||
Usually, it's in response to repetitive code appearing across a wide range of
|
||||
function definitions. A decorator can move that code to a central definition.
|
||||
|
||||
## Exercises
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
[Contents](../Contents) \| [Previous (7.4 Decorators)](04_Function_decorators) \| [Next (8 Testing and Debugging)](../08_Testing_debugging/00_Overview)
|
||||
|
||||
# 7.5 Decorated Methods
|
||||
|
||||
This section discusses a few common decorators that are used in
|
||||
This section discusses a few built-in decorators that are used in
|
||||
combination with method definitions.
|
||||
|
||||
### Predefined Decorators
|
||||
@@ -8,7 +10,7 @@ combination with method definitions.
|
||||
There are predefined decorators used to specify special kinds of methods in class definitions.
|
||||
|
||||
```python
|
||||
class Foo(object):
|
||||
class Foo:
|
||||
def bar(self,a):
|
||||
...
|
||||
|
||||
@@ -29,8 +31,9 @@ Let's go one by one.
|
||||
|
||||
### Static Methods
|
||||
|
||||
`@staticmethod` is used to define a so-called *static* class methods (from C++/Java).
|
||||
A static method is a function that is part of the class, but which does *not* operate on instances.
|
||||
`@staticmethod` is used to define a so-called *static* class methods
|
||||
(from C++/Java). A static method is a function that is part of the
|
||||
class, but which does *not* operate on instances.
|
||||
|
||||
```python
|
||||
class Foo(object):
|
||||
@@ -42,17 +45,19 @@ class Foo(object):
|
||||
>>>
|
||||
```
|
||||
|
||||
Static methods are sometimes used to implement internal supporting code for a class.
|
||||
For example, code to help manage created instances (memory management, system resources, persistence, locking, etc).
|
||||
Static methods are sometimes used to implement internal supporting
|
||||
code for a class. For example, code to help manage created instances
|
||||
(memory management, system resources, persistence, locking, etc).
|
||||
They're also used by certain design patterns (not discussed here).
|
||||
|
||||
### Class Methods
|
||||
|
||||
`@classmethod` is used to define class methods.
|
||||
A class method is a method that receives the *class* object as the first parameter instead of the instance.
|
||||
`@classmethod` is used to define class methods. A class method is a
|
||||
method that receives the *class* object as the first parameter instead
|
||||
of the instance.
|
||||
|
||||
```python
|
||||
class Foo(object):
|
||||
class Foo:
|
||||
def bar(self):
|
||||
print(self)
|
||||
|
||||
@@ -71,7 +76,7 @@ class Foo(object):
|
||||
Class methods are most often used as a tool for defining alternate constructors.
|
||||
|
||||
```python
|
||||
class Date(object):
|
||||
class Date:
|
||||
def __init__(self,year,month,day):
|
||||
self.year = year
|
||||
self.month = month
|
||||
@@ -90,7 +95,7 @@ d = Date.today()
|
||||
Class methods solve some tricky problems with features like inheritance.
|
||||
|
||||
```python
|
||||
class Date(object):
|
||||
class Date:
|
||||
...
|
||||
@classmethod
|
||||
def today(cls):
|
||||
@@ -106,62 +111,7 @@ d = NewDate.today()
|
||||
|
||||
## Exercises
|
||||
|
||||
Start this exercise by defining a `Date` class. For example:
|
||||
|
||||
```
|
||||
>>> class Date(object):
|
||||
def __init__(self,year,month,day):
|
||||
self.year = year
|
||||
self.month = month
|
||||
self.day = day
|
||||
|
||||
>>> d = Date(2010, 4, 13)
|
||||
>>> d.year, d.month, d.day
|
||||
(2010, 4, 13)
|
||||
>>>
|
||||
```
|
||||
|
||||
### Exercise 7.11: Class Methods
|
||||
|
||||
A common use of class methods is to provide alternate constructors
|
||||
(epecially since Python doesn't support overloaded methods). Modify
|
||||
the `Date` class to have a class method `today()` that creates a date
|
||||
from today's date.
|
||||
|
||||
```python
|
||||
>>> import time
|
||||
>>> class Date(object):
|
||||
def __init__(self,year,month,day):
|
||||
self.year = year
|
||||
self.month = month
|
||||
self.day = day
|
||||
@classmethod
|
||||
def today(cls):
|
||||
t = time.localtime()
|
||||
return cls(t.tm_year, t.tm_mon, t.tm_mday)
|
||||
|
||||
>>> d = Date.today()
|
||||
>>> d.year, d.month, d.day
|
||||
... output varies. Should be today ...
|
||||
>>>
|
||||
```
|
||||
|
||||
One reason you should use class methods for this is that they work
|
||||
with inheritance. For example, try this:
|
||||
|
||||
```python
|
||||
>>> class CustomDate(Date):
|
||||
def yow(self):
|
||||
print('Yow!')
|
||||
|
||||
>>> d = CustomDate.today()
|
||||
<__main__.CustomDate object at 0x10923d400>
|
||||
>>> d.yow()
|
||||
Yow!
|
||||
>>>
|
||||
```
|
||||
|
||||
### Exercise 7.12: Class Methods in Practice
|
||||
### Exercise 7.11: Class Methods in Practice
|
||||
|
||||
In your `report.py` and `portfolio.py` files, the creation of a `Portfolio`
|
||||
object is a bit muddled. For example, the `report.py` program has code like this:
|
||||
@@ -186,7 +136,7 @@ and the `portfolio.py` file defines `Portfolio()` with an odd initializer
|
||||
like this:
|
||||
|
||||
```python
|
||||
class Portfolio(object):
|
||||
class Portfolio:
|
||||
def __init__(self, holdings):
|
||||
self.holdings = holdings
|
||||
...
|
||||
@@ -202,7 +152,7 @@ Like this:
|
||||
|
||||
import stock
|
||||
|
||||
class Portfolio(object):
|
||||
class Portfolio:
|
||||
def __init__(self):
|
||||
self.holdings = []
|
||||
|
||||
@@ -222,7 +172,7 @@ class method for it:
|
||||
import fileparse
|
||||
import stock
|
||||
|
||||
class Portfolio(object):
|
||||
class Portfolio:
|
||||
def __init__(self):
|
||||
self.holdings = []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user