Continued, see the beginning
here .
In this section, we will talk in more detail about the definition of functions, and reveal some Python-specific features of this process. Since there is a lot of information, I will try to present everything quite briefly.
Default options
For all parameters of functions, you can specify default values, this makes it possible to call a function with a smaller number of parameters. For example, we have a function to authorize a user on the site:
def login(username="anonymous", password=None): """ - """ pass # # login("root", "ujdyzysqgfhjkm") login("guest") login() # , # login(password="nobody@mail.com")
In general, those parameters that are - are compared from left to right (if the name is not specified specifically), the rest are replaced by default values ​​(if they are given of course).
An important feature in this case is that default values ​​are calculated and associated only once - at the time of the function declaration. All the resulting shortcomings clearly demonstrate an example:
def_val = 2 def our_func(val = def_val): print val our_func(4)
A more unpleasant consequence of this. Suppose we want to declare a function that takes a list at the input, does something with it and prints it. And if the list is not specified, then by default it is equal to empty.
Attempting to do this "in the forehead" will not work exactly as I would like:
In [1]: def lister(lst = []): ...: lst.append([1, 2, 3]) ...: print lst ...: In [2]: lister() [[1, 2, 3]] In [3]: lister() [[1, 2, 3], [1, 2, 3]] In [4]: lister() [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
Actually, the problem here is that the lst variable will be associated with the empty list once, and it will keep its value between calls.
In this case, it will be correct to describe our function as follows (as all textbooks recommend):
In [5]: def lister2(lst=None): ...: if lst is None: ...: lst=[] ...: lst.append([1, 2, 3]) ...: print lst ...: In [6]: lister2() [[1, 2, 3]] In [7]: lister2() [[1, 2, 3]] In [8]: lister2([4, 5, 6]) [4, 5, 6, [1, 2, 3]]
This function will just work as I would like initially.
')
Position and keyword arguments.
Often there is a need to make a function that processes an indefinite number of parameters. For example, the function of calculating the sum of the elements of the list.
Of course, we can pass all the arguments as one parameter of type list, but it looks ugly. Therefore, in Python, a special mechanism was invented, called position-arguments. Here is an example demonstrating usage.
In [9]: def list_sum(*args): ...: smm = 0 ...: for arg in args: ...: smm += arg ...: return smm ...: In [10]: list_sum(1, 2, 3) Out[10]: 6 In [11]: list_sum(1) Out[11]: 1 In [12]: list_sum() Out[12]: 0
In this case, all our parameters are “packed” into the list of args in accordance with their “sequence number” during transmission.
The inverse operation is also possible. Suppose we have a list of values, and we want to pass them as a list of function parameters:
In [14]: lst = [1, 10, 2] In [15]: list(range(*lst)) Out[15]: [1, 3, 5, 7, 9]
In this example, the lst list was “unpacked” and substituted for the parameters of the range function, that is, the call was similar:
In [16]: list(range(1, 10, 2)) Out[16]: [1, 3, 5, 7, 9]
In addition to position, you can use the so-called. keyword arguments. They are distinguished by the fact that they must be explicitly given a name. Here is an example - a function that generates an insert expression for the database (NB: maximum optimization was not set in this case as an end in itself).
def enquote1(in_str): """Quotes input string with single-quote""" in_str = in_str.replace("'", r"\'") return "'%s'" % in_str def enquote2(in_str): """Quotes input string with double-quote""" in_str = in_str.replace('"', r'\"') return '"%s"' % in_str def gen_insert(table, **kwargs): """Generates DB insert statement""" cols = [] vals = [] for col, val in kwargs.items(): cols.append(enquote2(col)) vals.append(enquote1(str(val))) cols = ", ".join(cols) vals = ", ".join(vals) return 'INSERT INTO "%s"(%s) VALUES(%s);' % ( table, cols, vals) print gen_insert("workers", name="John", age=21, salary=1500.0) params = {"name": "Mary", "age": 19, "salary": 1200.0} print gen_insert("workers", **params)
At the output we get what was expected:
INSERT INTO "workers"("salary", "age", "name") VALUES('1500.0', '21', 'John'); INSERT INTO "workers"("salary", "age", "name") VALUES('1200.0', '19', 'Mary');
Pay attention to the second function call gen_insert - so we can call a function with only a dictionary of parameters. This applies to any function. Various combinations of positional and keyword arguments are also possible.
As a final example, consider the following function:
def logging(func, *args, **kwargs): res = func(*args, **kwargs) print 'calling %s, returned %r' % (func.__name__, res) return res def double(x): "Doubles a number" return 2*x print logging(double, 155)
This is the easiest way to debug a function, we kind of "wrap" the call of one function to another to output intermediate information.
Looking ahead - I will say that in Python there is a very powerful tool for more convenient use of this kind of “wrapping” functions, called decorators, but more on that later.
That's all for today. To be continued (either mine or the distinguished
Gerlion ), stay with us.
"Homework"
Modify the logging function, adding to it the output of parameters with which the child function is called, and the handling of possible exceptions in the child function.