python: advanced features of functions

Many languages ​​allow functions to be passed as parameters to other parameters: so-called higher-order functions. There are similar features in python:

一、map/reduce、filter、sorted

The idea of ​​map-reduce in hadoop has become a built-in function in python. map is to apply a function to each element in the list one by one. reduce first takes the first two elements from the list, passes them to the specified function, and then repeats the calculation result with the remaining elements in turn until the list is processed.
 
1.1 map example: (all elements in the List * 10)
def fn_map(x):
    print("fn_map->", x)
    return 10 * x


L = [3, 4, 6, 8]

print(list(map(fn_map, L)))
print("\n")

output:

fn_map-> 3
fn_map-> 4
fn_map-> 6
fn_map-> 8
[30, 40, 60, 80]

Combined with map, we add the reduce function (final effect: square all elements * 10, and finally get the "square root" of the "sum of squares")

def fn_sqrt(x, y):
    print("fn_sqrt->", x, ",", y)
    return math.sqrt(x ** 2 + y ** 2)


def fn_map(x):
    print("fn_map->", x)
    return 10 * x


L = [3, 4, 6, 8]

result = reduce(fn_sqrt, map(fn_map, L))
print(result)

print("\n")
print(math.sqrt((3 * 10) ** 2 + (4 * 10) ** 2 + (6 * 10) ** 2 + (8 * 10) ** 2))

 Note: To import math first, the output of the above code is as follows:

fn_map-> 3
fn_map-> 4
fn_sqrt-> 30 , 40
fn_map-> 6
fn_sqrt-> 50.0 , 60
fn_map-> 8
fn_sqrt-> 78.10249675906654 , 80
111.80339887498948


111.80339887498948

The above example may not be very practical. Let's give a more practical example, capitalize the first letter of each word, and lowercase other letters.

def normalize(name):
    return name[:1].upper() + name[1:].lower()


L1 = ['adam', 'LISA', 'barT']
print(list(map(normalize, L1)))

output:

['Adam', 'Lisa', 'Bart']

 

1.2 filter

The filter is similar to the stream filter in java8, which can filter the elements in the collection according to certain rules.

Example 1: Find even numbers within 10

result = filter(lambda x: x % 2 == 0, range(1, 11))
print(list(result))


# The above writing is equivalent to the following
def even(x):
    return x % 2 == 0


print(list(filter(even, range(1, 11))))

output:

[2, 4, 6, 8, 10]
[2, 4, 6, 8, 10]

Example 2: Find the "number of times" within 200 (ie: from left to right, from right to left, all are the same number, such as: 131, 141)

def is_palindrome1(n):
    if n < 10:
        return True
    s = str(n)
    for i in range(0, int(len(s) / 2)):
        if s[i] == s[-i - 1]:
            return True
    return False


def is_palindrome2(n):
    s1 = str(n)
    s2 = list(reversed(s1))
    return list(s1) == s2


print(list(filter(is_palindrome1, range(1, 201))))
print(list(filter(is_palindrome2, range(1, 201))))

output:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]

  

1.3 sorted

The built-in sorting function sorted in python supports the sorting of numbers/letters/ and complex objects. The default is to sort from small to large. The sorting rules for complex objects can be customized by developers. Refer to the example below:

origin = [-1, 3, -5, 2, -4, 6]
# Sort from smallest to largest
a = sorted(origin)
print(a)

# Sort by absolute value of abs, from small to large
a = sorted(origin, key=abs)
print(a)

# Sort from largest to smallest
a = sorted(origin, reverse=True)
print(a)

origin = ["Xy", "Aa", "Bb", "dd", "cC", "aA", "Zo"]

# Sort alphabetically by ascii value from smallest to largest
print(sorted(origin))

# Sort the values ​​after converting the letters to uppercase (ie: ignore case)
print(sorted(origin, key=str.upper))

# Reverse sort the values ​​after capitalizing the letters
print(sorted(origin, key=str.upper, reverse=True))

# complex object sorting
origin = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]


def by_name(t):
    return t[0]


# Sort by name
print(sorted(origin, key=by_name))


def by_score(t):
    return t[1]


# Sort by score
print(sorted(origin, key=by_score, reverse=True))

output:

[-5, -4, -1, 2, 3, 6]
[-1, 2, 3, -4, -5, 6]
[6, 3, 2, -1, -4, -5]
['Aa', 'Bb', 'Xy', 'Zo', 'aA', 'cC', 'dd']
['Aa', 'aA', 'Bb', 'cC', 'dd', 'Xy', 'Zo']
['Zo', 'Xy', 'dd', 'cC', 'Bb', 'Aa', 'aA']
[('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]
[('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]

 

2. Delayed computation/closure

Python's function definitions can be nested (ie: functions are defined inside functions), and it is easy to implement delayed computation using this feature :

import time


def export1(month):
    print("export1 month:", month, " doing...")
    time.sleep(5)
    print("export1 done!")


def export2(month):
    def do():
        print("export2 month:", month, " doing...")
        time.sleep(5)
        print("export2 done!")

    return do


export1(10)

print("----------------")

r2 = export2(10)
print(r2)
r2 ()

Here we simulate a time-consuming export function (assume: the month is required to be passed in, and then the report data of that month is exported), export1 is the regular version, and the call to export1 will be executed immediately. And export2 returns an internal function do(). After calling export2, it returns a Function, which is not actually executed (it can be understood as: what is returned is the business processing algorithm, not the processing result). When the result is really needed, Then call the "return function".

The output of the above code is as follows:

export1 month: 10  doing...
export1 done!
----------------
<function export2.<locals>.do at 0x107a24a60>
export2 month: 10  doing...
export2 done!

  

Closure

Many languages ​​support the closure feature. Of course, this is indispensable in python. Refer to the following example:

def my_sqrt1(n):
    r = []

    def do():
        for i in range(1, n + 1):
            r.append(i ** 2)
        return r

    return do


a = my_sqrt1(4)
print(type(a))
b = a()
print(type(b))
print(b)

output:

<class 'function'>
<class 'list'>
[1, 4, 9, 16]  

Closures have a classic pitfall: don't use "variables whose value changes" (eg: variables in for loops) in closure functions . The reason is: the closure in python is essentially an "internal function" delayed calculation. If there is a loop variable, the closure function will not be executed during the loop. When the loop is over, the loop variable referenced in the closure is actually The final value after the loop ends. It's a bit of a twist, but here's an example:

def my_sqrt2(n):
    r = []
    for i in range(1, n + 1):
        def do():
            r.append(i ** 2)
            return r

    return do


a = my_sqrt2(4)
print(type(a))
b = a()
print(type(b))
print(b)

output:

<class 'function'>
<class 'list'>
[16]

Explain: when calling a = my_sqrt2(4), my_sqrt2(4) will be executed immediately. At this time, the fox loop inside will be executed. Finally, the value of i will stop at 4, and then this value will be enclosed in the do function, and will not be executed immediately. implement. Then when a() is called again, the do() function is actually called at this time. At this time, the i value = 4, so in the final r[] list, only one value 4*4=16 is recovered. 

If you have to use the loop variable, you can only try to enclose the loop variable in an internal function, and then use it again, such as the following:

def my_sqrt3(n):
    def f(j):
        def g():
            return j ** 2

        return g

    r = []
    for i in range(1, n + 1):
        r.append(f(i))

    return r


a = my_sqrt3(4)
print(type(a))

for x in a:
    print(x())

It is interesting to study this example carefully, r.append(f(i)), what is appended to the list is not the calculation result, but the function g returned in f(j), so a = my_sqrt3(4) Here, a gets is a list composed of functions, and then each instance of the g function in the list closes the variable i of the current loop. Because of the closure, the value of i has been sealed inside g, no matter how the external for loop variable , will not affect the function g.

The output is as follows:

<class 'list'>
1
4
9
16

Finally, let's look at a closure homework problem on Mr. Liao's tutorial, and write a counter in the way of closure:

def create_counter1():
    r = [0]

    def counter():
        r[0] += 1
        return r[0]

    return counter


count = create_counter1();

print([count(), count(), count()])

output:

[1, 2, 3] 

For programmers with cleanliness, it may feel that it is a bit wasteful to set up an additional list that only saves one element. It can be written differently:

def create_counter2():
    n = 0

    def counter():
        nonlocal n
        n += 1
        return n

    return counter

count = create_counter2();

print([count(), count(), count()])

output:

[1, 2, 3]  

Note that there is a keyword nonlocal , which is known as a new keyword introduced by python3, in order to allow the inner function of the closure to read and write variables outside the inner function. (But in the first writing method, isn't r=[0] also defined externally? The difference is that list is a complex variable type, while in the second writing method n is a simple type variable. As a python beginner, no I understand this philosophy very well ^_~)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325257143&siteId=291194637