一、函数

1、函数是什么?

函数一词来源于数学,但编程中的「函数」概念,与数学中的函数是有很大不同的,具体区别,我们后面会讲,编程中的函数在英文中也有很多不同的叫法。在BASIC中叫做subroutine(子过程或子程序),在Pascal中叫做procedure(过程)和function,在C中只有function,在Java里面叫做method。

定义: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可

特性:

  1. 减少重复代码
  2. 使程序变的可扩展
  3. 使程序变得易维护
语法定义



def sayhi():#函数名
    print("Hello, I'm nobody!")
 
sayhi() #调用函数



可以带参数



#下面这段代码
a,b = 5,8
c = a**b
print(c)
 
 
#改成用函数写
def calc(x,y):
    res = x**y
    return res #返回函数执行结果
 
c = calc(a,b) #结果赋值给c变量
print(c)



2、函数参数

形参:变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量

实参:可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数

获得确定值。

3、默认参数

看下面代码:



def stu_register(name,age,country,course):
    print("----注册学生信息------")
    print("姓名:",name)
    print("age:",age)
    print("国籍:",country)
    print("课程:",course)
 
stu_register("王山炮",22,"CN","python_devops")
stu_register("张叫春",21,"CN","linux")
stu_register("刘老根",25,"CN","linux")



发现 country 这个参数 基本都 是"CN", 就像我们在网站上注册用户,像国籍这种信息,你不填写,默认就会是 中国, 这就是通过默认参数实现的,把country变成默认参数非常简单



def stu_register(name,age,course,country="CN"):



这样,这个参数在调用时不指定,那默认就是CN,指定了的话,就用你指定的值。



另外,你可能注意到了,在把country变成默认参数后,我同时把它的位置移到了最后面,为什么呢?  



4、关键参数

正常情况下,给函数传参数要按顺序,不想按顺序就可以用关键参数,只需指定参数名即可,但记住一个要求就是,关键参数必须放在位置参数之后。



stu_register(age=22,name='alex',course="python",)



5、非固定参数

若你的函数在定义时不确定用户想传入多少个参数,就可以使用非固定参数



def stu_register(name,age,*args): # *args 会把多传入的参数变成一个元组形式
    print(name,age,args)
 
stu_register("Alex",22)
#输出
#Alex 22 () #后面这个()就是args,只是因为没传值,所以为空
 
stu_register("Jack",32,"CN","Python")
#输出
# Jack 32 ('CN', 'Python')



还可以有一个**kwargs



def stu_register(name,age,*args,**kwargs): # *kwargs 会把多传入的参数变成一个dict形式
    print(name,age,args,kwargs)
 
stu_register("Alex",22)
#输出
#Alex 22 () {}#后面这个{}就是kwargs,只是因为没传值,所以为空
 
stu_register("Jack",32,"CN","Python",sex="Male",province="ShanDong")
#输出
# Jack 32 ('CN', 'Python') {'province': 'ShanDong', 'sex': 'Male'}



函数调用



# 动态参数
def show(*args,**kwargs):
    print(args,type(args))
    print(kwargs,type(kwargs))

# 普通的调用
show(11,22,33,key1='aa',key2='bb')

# 在调用时传入列表和字典
li = [11,22,33]
di = {'key1':'aa','key2':'bb'}
show(*li,**di)



str.format的案例使用



str1 = '{0} is {1}'
rest = str1.format('alex','sb')
print(rest)

str2 = '{name} is {action}'
rest = str2.format(name='alex',action='sb')
print(rest)



6、局部变量



name = "Alex Li"
 
def change_name(name):
    print("before change:",name)
    name = "金角大王,一个有Tesla的男人"
    print("after change", name)
 
 
change_name(name)
 
print("在外面看看name改了么?",name)



输出



before change: Alex Li
after change 金角大王,一个有Tesla的男人
在外面看看name改了么? Alex Li



 7、函数的返回值

要想获取函数的执行结果,就可以用return语句把结果返回

注意:

  1. 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,so 也可以理解为 return 语句代表着函数的结束
  2. 如果未在函数中指定return,那这个函数的返回值为None
  3. 函数在返回多个值是,python会自动将多个值封装成元祖返回
import math

# 函数的多个参数以及多个返回值
def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y + step * math.sin(angle)
    return nx, ny   # 返回多个值时,python自动将多个值封装成元组返回


print(move(30,40,6,45))   # (33.15193193290638, 45.10542114720471)
nx,ny = move(30,40,6,45)
print(nx,ny)              # 33.15193193290638 45.10542114720471



8、函数的递归

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。

递归特性:

1. 必须有一个明确的结束条件

2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少

3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)



def calc(n):
    print(n)
    if int(n/2) == 0:   # 此次的条件为n为1时,退出循环.
        return n
    else:
        return calc(int(n / 2))



递归函数实际应用案例,二分查找(二分查找只适合已排序的列表):



def binary_search(dataset,find_num):
    print(dataset)
    if len(dataset) > 1:
        mid = int(len(dataset)/2)
        if dataset[mid] == find_num:  #find it
            print("找到数字",dataset[mid])
        elif dataset[mid] > find_num:
            print("\033[31;1m找的数在mid[%s]左面\033[0m" % dataset[mid])
            return binary_search(dataset[:mid],find_num)
        else:
            print("\033[32;1m找的数在mid[%s]右面\033[0m" % dataset[mid])
            return binary_search(dataset[mid+1:], find_num)
    else:
        if dataset[0] == find_num:  #find it
            print("找到数字啦",dataset[0])
        else:
            print("要找的数字[%s]不在列表里" % find_num)
            
data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]
binary_search(data,12)



 非递归方式的实现:



def binary_search2(dataset,find_num):
    start = 0
    end = len(dataset) - 1
    while start <= end :
        print(dataset[start:end+1])
        mid = int((start + end)/2)
        if dataset[mid] == find_num:  # find it
            print("找到数字", dataset[mid])
            return dataset[mid]
        elif dataset[mid] > find_num:
            print("\033[31;1m找的数在mid[%s]左面\033[0m" % dataset[mid])
            end = mid - 1
        else:
            print("\033[32;1m找的数在mid[%s]右面\033[0m" % dataset[mid])
            start = mid + 1
    print("要找的数字[%s]不在列表里" % find_num)
    
data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]
binary_search2(data,12)



 

9、函数的嵌套

 

10、匿名函数(lambda表达式

在讲lambda之前先看一下三元运算符



# 普通条件语句
if 1 == 1:
    name = 'luotianshuai'
else:
    name = 'shuaige'



以上简单的逻辑判断可以使用三元运算符表示:



# 三元运算
name = 'luotianshuai' if 1 == 1 else 'shuaige'



lambda语句中,冒号前是参数,可以有多个,用逗号隔开,冒号右边的返回值。lambda语句构建的其实是一个函数对象。



foo = lambda x,y,z : x+y+z



以上lambda表达式和以下函数的效果是一样的:



def foo(x,y,z):
    return x + y + z



12、内置参数

Python 提供了一个名为 resource 的模块 python提供的函数称为_数据结构与算法

map和filter



#!/usr/bin/env python3

num_list = [11,22,33,44]
# map:将所以列表集体操作,返回新的列表
new_num_list = map(lambda x:x+100,num_list)
print(list(new_num_list))

# filter: 将满足条件的放入新的列表中
new_num_list = filter(lambda x:True if x>22 else False,num_list)
print(list(new_num_list))



验证码:



#!/usr/bin/env python3
import random

verify_code = []
for i in range(4):
    random_num = random.randint(1, 9)
    if random_num < 5:
        random_code = random.randint(66,90)
        verify_code.append(chr(random_code))
    else:
        verify_code.append(str(random_num))

print(''.join(verify_code))



二、迭代器

如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断.



print(isinstance('abc', Iterable))           # 字符串是否可迭代:True
print(isinstance(['a','b','c'], Iterable))   # 列表是否可迭代:True
print(isinstance(123, Iterable))             # 数字是否可迭代:False



对字典进行迭代操作



user_dict = {'username':'Alex','age':18,'job':'Engineer'}
# 迭代字典得到key,通过key获取value
for key in user_dict:
    value = user_dict[key]
    print(key,':', value)

# 迭代字典中所有的key和value
for key, value in user_dict.items():
    print(key, '=', value)

# 迭代字典中所有的key
for key in user_dict.keys():
    print(key)

# 迭代字典中所有的value
for value in user_dict.values():
    print(value)



对字符串进行迭代操作



str = 'ABCDEFG'
for ch in str:
    print(ch)

#result
A
B
C
D
E
F
G



最后一个小问题,如果要对list实现类似Java那样的下标循环怎么办?Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身.



username_list = ['Alex', 'Eric', 'Kingle', 'Magic']

for index, username in enumerate(username_list):
    print(index, username)

for index, username in enumerate(username_list, 100):
    print(index, username)



 

三、生成器

生成器:一个函数在调用时返回一个迭代器,那么这个函数就叫做生成器,那这个函数就会变成生成器



def cash_money(amount):
    while amount > 0:
        amount -= 100
        yield 100
        print('又来取钱了....')

atm = cash_money(500)
print(atm.__next__())
print(atm.__next__())
print(atm.__next__())



使用yield实现单线程中的异步并发效果(生产者和消费者的案例)



import time
def consumer(name):
    print('准备处理用户%s请求'% name)
    while True:
        req = yield
        print('正在处理[%s]的第[%s]请求'%(name,req))

def producer():
    alex = consumer('Alex')
    eric = consumer('Elex')
    alex.__next__()
    eric.__next__()
    for i in range(10):
        time.sleep(1)
        print('同时并发2个请求')
        alex.send(i)
        eric.send(i)

producer()



 

四、装饰器



#!/usr/bin/env python3

# 装饰器原理解析1
def login(func):
    print('login sucessful ...')
    return func

def index(username):
    print('%s,welcome to python ...' %username)

index = login(index)
index('zhangming')

# 装饰器原理解析2:以上在不执行index('zhangming')函数时,也会执行login验证,需要进行以下改造.
def login(func):
    def inner(arg):
        print('login sucessful ...')
        func(arg)
    return inner

def index(username):
    print('%s,welcome to python ...' %username)

index = login(index)
index('zhangming')      # 此时执行的是inner函数

# 装饰器的使用
def login(func):
    def inner(arg):
        print('login sucessful ...')
        func(arg)
    return inner

@login # 等同于 index = login(index)
def index(username):
    print('%s,welcome to python ...' %username)

#index = login(index)
index('zhangming')

# 装饰器的使用:装饰器的返回值和参数
def login(func):
    def inner(*args,**kwargs):
        print('login sucessful ...')
        return func(*args,**kwargs)
    return inner

@login
def index(username,password):
    print('%s,welcome to python ...' %username)
    return True

#index = login(index)
stats = index('zhangming','123456')
print(stats)



 

五、列表生成表达式



#!/usr/bin/env python3
# -*- coding: utf-8 -*-

'''
列表生成式
'''

# 生成一个[1, 2, 3, ..., 10]列表
print([x for x in range(1,11)])
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 生成一个[1x1, 2x2, 3x3, ..., 10x10]列表
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
print([x*x for x in range(1, 11)])
# 生成一个[1+1, 2+2, 3+3, ..., 10+10]列表
print([x+x for x in range(1, 11)])
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
# 生成一个[1x1, 2x2, 3x3, ..., 10x10]列表中所有的偶数
print([x*x for x in range(1, 11) if x%2 == 0])
[4, 16, 36, 64, 100]

# 使用两层循环,可以生成全排列.
print([x+y for x in 'ABC' for y in 'XYZ'])
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

# 列出当前目录下的所有文件和目录名,可以通过一行代码实现.
import os
print([dir for dir in os.listdir('.')])

# 列表生成式也可以使用两个变量来生成list
test_dict = {'x': 'A', 'y': 'B', 'z': 'C' }
print([k + '=' + v for k,v in test_dict.items()])
['y=B', 'x=A', 'z=C']

# 将list中所有的字符串变成小写:
test_list = ['Hello', 'World', 'IBM', 'Apple']
print([l.lower() for l in test_list])
['hello', 'world', 'ibm', 'apple']

# 输出L1列表中的所有字符串
l1 = ['Hello', 'World', 18, 'Apple', None]
print([s for s in l1 if isinstance(s,str)])
['Hello', 'World', 'Apple']

# 生成一个4*4二维数组
l2 = [ [ i for i in range(4)] for j in range(4) ]
for l in l2:
    print(l)
[0, 1, 2, 3]
[0, 1, 2, 3]
[0, 1, 2, 3]
[0, 1, 2, 3]



 

案例:生成一个4*4的二维数组,将其旋转90度



# 99乘法表
for i in range(10):
    for j in range(i+1):
        print('%d*%d=%d' %(i, j, i * j),end='\t'),
    print('')


# 列表90度旋转
data = [[i for i in range(4)] for j in range(4) ]
for d in data:
    print(d)
print('----')
for i in range(len(data)):
    for j in range(i,len(data[i])):
        tmp = data[i][j]
        data[i][j] = data[j][i]
        data[j][i] = tmp

        print('[%d][%d]<=>[%d][%d]' % (i,j,j,i))
    print('----')

for d in data:
    print(d)



案例:冒泡排序



data = [1,25,2,45,65,10,56,85,45,23]
for i in range(1,len(data)):
    for j in range(len(data)-i):
        if data[j] > data[j+1]:
            tmp = data[j]
            data[j] = data[j+1]
            data[j+1] = tmp

print(data)



 

练习:



#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os,re,sys

def show_backend_server(file,backend_name):
    server_flag = False
    with open(file, 'r') as read_file:
        for line in read_file.readlines():
            if re.match('backend', line):
                if re.split('\s+', line)[1] == backend_name:
                    server_flag = True
                else:
                    server_flag = False
            if server_flag and re.match('\s+server', line):
                print(line.replace('\n',''))

def add_backend_server(file,handle_dict):
    newfile = '%s.new' % file
    backend_flag = False
    server_flag = False
    server_li = []
    with open(file, 'r') as read_file, open(newfile, 'w') as write_file:
        for line in read_file:
            line_li = line.strip().split(' ')
            if line_li[0] == 'backend' and line_li[1] == handle_dict['backend']:
                write_file.write(line)
                backend_flag = True
                continue

            if backend_flag and re.match('\s+server', line):
                server_li.append(line)
                server_flag = True
                continue

            if server_flag:
                server_dict = handle_dict['record']
                server_line = '\tserver {name} {server} weight {weight} maxconn {maxconn}\n'.format(**server_dict)
                if server_line not in server_li:
                    server_li.append(server_line)
                for l in server_li:
                    write_file.write(l)
                server_flag = False

            write_file.write(line)
            backend_flag = False

    os.remove(file)
    os.rename('%s.new' % file,file)

def del_backend_server(file,handle_dict):
    newfile = '%s.new' % file
    backend_flag = False
    server_flag = False
    server_li = []
    with open(file, 'r') as read_file, open(newfile, 'w') as write_file:
        for line in read_file:
            line_li = line.strip().split(' ')
            if line_li[0] == 'backend' and line_li[1] == handle_dict['backend']:
                write_file.write(line)
                backend_flag = True
                continue

            if backend_flag and re.match('\s+server', line):
                server_li.append(line)
                server_flag = True
                continue

            if server_flag:
                server_dict = handle_dict['record']
                server_line = '\tserver {name} {server} weight {weight} maxconn {maxconn}\n'.format(**server_dict)
                if server_line in server_li:
                    server_li.remove(server_line)
                for l in server_li:
                    write_file.write(l)
                server_flag = False

            write_file.write(line)
            backend_flag = False

    os.remove(file)
    os.rename('%s.new' % file, file)

def user_input():
    backend_name = input('请输入backend:')
    name = input('请输入服务器的名字:')
    server = input('请输入服务器的IP:')
    weight = input('请输入服务器的权重:')
    maxconn = input('请输入服务器的最大连接数:')
    record = {'name': name, 'server': server, 'weight': weight, 'maxconn': maxconn}
    handle_dict = {'backend': backend_name, 'record': record}
    return handle_dict


if __name__ == '__main__':
    while True:
        print(
        '''
        选择:
            1、获取ha记录
            2、增加ha记录
            3、删除ha记录
            4、退出
        ''')

        choice = input('请输入操作序号:')

        if choice == '1':
            backend_name = input('请输入backend:')
            show_backend_server('conf/ha.txt', backend_name)
        elif choice == '2' :
            handle_dict = user_input()
            add_backend_server('conf/ha.txt', handle_dict)
        elif choice == '3':
            handle_dict = user_input()
            # {'backend': 'www.oldboy.org', 'record': {'name': '100.1.7.9', 'server': '100.1.7.9', 'weight': '20', 'maxconn': '20'}}
            print(handle_dict)
            del_backend_server('conf/ha.txt', handle_dict)
        elif choice == '4':
            sys.exit()
        else:
            pass