Use Skills of Python Functions

Functions that can accept any number of parameters

Defining a function in Python is very simple, such as a function that calculates a quadratic power:

def power(x):
    return x * x

If we want a function that can accept any number of parameters, how do we define it?
Let's say you want a function that accepts a set of data and calculates the sum of their squares.
Of course, you can pass directly the parameters of a list or tuple as a function:

def sum_power(x):
    sum = 0
    for i in x:
        sum += i * i
    return sum

But to do so, if you want to use this function, you must first make a list or tuple, like this call:

sum_power([1,2,3,4])

It is much simpler to use the following method:

def sum_power(*x):
    sum = 0
    for i in x:
        sum += i * i
    return sum

With the parameter at the beginning of * the position parameter after that will be treated as a sequence.
But what if you have a list arr = 1, 2, 3, 4, 5?
This function can be called as follows:

sum_power(*arr)

When a function is called, adding a * to a parameter such as a list is equivalent to splitting the list and passing in all elements as parameters of the function.

If you want to use any number of keyword parameters, you can use the parameters beginning with **.

>>> def a(**attrs):
...     print(attrs)
...
>>> a(name="Zhang San", age="18")
{'name': 'Zhang San', 'age': '18'}
>>>

It can be found that parameters starting with ** are packaged into dictionaries for processing.

def a(*args, **kwargs):
    pass

Functions like this can accept any number of positional and keyword parameters.

keyword-only parameter

When defining a function, the parameter at the beginning of * must be the last position parameter, while the parameter at the beginning of ** must be the last parameter. Think about why?
However, after the parameter at the beginning of *, it is acceptable to put a keyword parameter after the function at the beginning of * or a single *. This parameter will become a keyword-only parameter, which can only be used in the form of keywords.
For example:

def person(name, *, age):
    pass
    
person("Zhang San", 12)  #Error: TypeError: person() takes 1 positional argument but 2 were given
person("Zhang San", age=12)  #True

At this time, the first way to call the function will be wrong, must be in the form of the keyword age=12.
Using keyword-only parameters can improve code readability.

closure

Sometimes for classes that have only one method (except _init_(), we want to replace it with a function to simplify the code.

from urllib.request import urlopen


class UrlTemplate:
    def __init__(self, template):
        self.template = template
    def open(self, **kwargs):
        return urlopen(self.template.format_map(kwargs))
        
# Use examples
yahoo = UrlTemplate('http://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}')
for line in yahoo.open(names='IBM,AAPL,FB', fields='sllclv'):
    print(line.decode('utf-8'))

The UrlTemplate class here can be replaced by functions:

def urltemplate(template):
    def opener(**kwargs):
        return urlopen(template.format_map(kwargs))
    return opener
    
# Use examples
yahoo = urltemplate('http://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}')
for line in yahoo(names='IBM,AAPL,FB', fields='sllclv'):
    print(line.decode('utf-8'))

Closure is a nested function, and external functions return internal functions as return values directly. Closure, as its name implies, is like a package that remembers the context in which it was defined. As in the example, the opener() function can remember the value of the urltemplate() parameter template and use it in subsequent calls.

Decorator

Sometimes we want to extend the function, but we don't want to modify the code of the function, so we can use the decorator. A decorator is essentially a function (or class) that accepts a function as input and returns a new function as output.
For example, we now need to add a log function to our function to print out the execution time of the function:

import time
from functools import wraps


def log(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("calling:{}".format(func.__name__))
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(func.__name__, end-start)
        return result
    return wrapper

# Use examples
@log
def f(x):
    while x < 1000000:
        x += 1

Call result:

>>> f(1)
calling:f
f 0.11393427848815918
>>> 

If we don't use decorator, we need to write code in f() function. Not only does the original function become totally different, but if we want to add the recording function to other functions, we have to copy the code in the past. It's better to use decorator conveniently.

The part on which the ornament is used is essentially equivalent to:

def f(x):
    ...
f = log(f)

The decorator function accepts the decorated function as a parameter and reassigns the variable of the original function name to the new function after returning.

Sweep code to pay attention to public numbers:

Keywords: Python

Added by rckehoe on Fri, 06 Sep 2019 15:06:24 +0300