Python函数

一、函数定义以及作用

函数/方法/function(功能):函数是组织好的,可重复使用的,用来实现单一或相关联功能的代码段(块)。函数能提高应用的模块性和代码的重复利用率。比如我们天天使用的函数print(),这个就是函数(一个Python内置的函数)。也可以自己创建函数,这类函数就叫做用户自定义函数。就是把一堆零散的代码放在一起并组织起来完成一个特定任务。

为什么需要函数:

载体------函数编程模式,让电脑的运算/逻辑以函数的方式进行呈现

组织------函数将原有零散的代码根据功能进行组织

复用------对高频次使用的代码段(功能)进行重复使用

封装------基于功能进行封装,无须关注内部细节。只需调用即可

清晰------提高整体代码本身的可读性

按需------需要的时候可随时调用(呼之则来,挥之则去)

(1)函数,数学定义y=f(x),y是x的函数,x是自变量。

(2)Python函数:由若干语句组成的语句块,函数名称、参数列表,组织代码的最小参数,完成一定的功能。

结构化编程是对代码的最基本的封装,一般按照功能组织一端代码。封装的目的是为了复用,减少冗余代码。代码更加简洁美丽,可读易懂。

二、函数的分类

(1) 内建函数:max()

(2) 库函数:math() ceil()等

三、函数的定义、调用

函数必须先声明再使用。函数名代表函数本身,定义需要在调用前,也就是说调用的时候已经被定义过了,否则会抛出异常的,声明函数的关键字/命令使用def函数。

def语句定义函数:

def 函数名(参数列表):
   函数体(代码块)
[return 返回值]*

函数名就是标示符,命名要求一样。语句必须缩进四个空格,注意缩进的从属关系。

Python的函数如果没有return语句,隐式会返回none值。

定义中的参数列表成为形式参数,只是一种符号表达,简称形参。

调用:函数定义,只是声明了一个函数,它不会被执行,需要调用才会生效。调用方式就是函数名加上小括号,括号内写上参数。调用时候的参数就是实际参数,是实实在在传入的值,成为实参。定义的时候叫做形参,调用的时候叫做实参。

def myfunction(a,b):
    return a+b
print(myfunction(1,2))
def myfunction(a,b):       #申明函数
    print('hello,python')
    return a+b
myresult=myfunction(1,2)   #调用函数赋值变量
print(myfunction(1,2))     #打印变量

函数是可调用的对象,利用callable()查询。调用通过函数名myfunction加两个参数,返回值可使用变量接收。计算的结果,通过返回值进行返回。必须加同类型的,复用的。

四、函数的参数

(1)参数调用时传入的参数要和定义的个数匹配(可变参数除外)。

(2)def f(x,y)调用的时候使用f(1,3),按照参数的定义位置顺序传入实参。

(3)关键字参数def(x,y,z)调用使用f(x=1,y=3,z=5),使用形参的名字来出入实参的方式,使用了形参的名字,顺序可以不一致。

(4)传参(定义在函数中使用的变量叫传参)

f(z=none,y=10,x=[1])     f((1,)    z=6,y=4.1)

要求位置参数必须在关键字参数之前,位置参数按照位置进行对应。位置在前,关键字在后。

形式参数:参数的名字(类似变量名)------parameter,形式参数一旦定义,必须要跟上足够数量的实参

实际参数:该参数的具体值(变量的那个值)------argument

位置参数:按照参数位置取值------positional

关键字参数:就是一个标准的变量赋值的过程

默认参数:为函数预设的参数值,默认参数一定要在非默认参数之后

参数组:可变参数和可变关键字-----传入结构化数据类型(列表、元组、字典)

向函数传入的具体值叫实参

def add(firstname,lastname):
    full_name=firstname+lastname
    print(full_name)
    return full_name
print(add('James','Bond'))   #位置参数,根据位置传入值
print(add(firstname='James',lastname='Bond'))

输出结果:

JamesBond

JamesBond

JamesBond

JamesBond

def circle_area(radius,pi=3.14):        # 函数预设值叫做默认参数,默认参数一定要在非默认参数之后
    area=pi*radius*2
    return area 
print(circle_area(radius=10))           # 保持默认参数
print(circle_area(10))
print(circle_area(radius=10,pi=3.1415927))
print(circle_area(10,3.1415927)) 

输出结果:

62.800000000000004

62.800000000000004

62.831854

62.831854

python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。

一旦位参和关键字参数同时传参的时候,要以位参开始

def testrun(name,age,height):
    print(name,age,height)
#testrun(name='mike',2,1.74)    #SyntaxError: positional argument follows keyword argument
testrun('mike',age=2,height=1.74)

可变参数

可变参数:拥有一星(*)的参数组(可变参数),多实参和单接受(形参)

1、单星号开始

2、星号后跟一个关键字

3、args是一个约定俗称的命名

4、args你可以自定义(不建议)

5、调用时传多值

6、*args接收后全部转为元组

7、值打印或者return都是元组

import math
def multi_values(*args):  
    print(args)
    print(sum(args))

multi_values(1,2,3,4,5)

输出:

(1, 2, 3, 4, 5)

15

注意:

1、*这里代表接收的参数数量不受限制(大于等于0个)

2、*args支持不传值,输出为空元祖

3、args=arguments代表多个实参

关键字参数和*args参数组(可变参数)混合使用 

def multi_vlaues_1(num1,*args):
    print(num1)
    print(args)
multi_vlaues_1(1,123,1,2,3)
def num_student(*args):
    print("学生的总数是:",len(*args))
    for student in *args:
        print("他们是:",student)
num_student('小明','大明','二明')

如果一定要对调两个形参,必须要给关键字参数  

def testrun(*args,num):
    print(num)
    print(args)
#testrun(1,2,3)    #这样写会报错,因为args吃掉了所有的实参
testrun(1,2,num=123)

带默认参数的情况

#带默认参数的情况
def testrun(num1,num2=100,*args):
    print(num1)
    print(num2)
    print(args)
list_data=[1,2,3]   
testrun(100,120,*list_data)
nums=[1,2,3,4,5,6,7,8,9,10]
def addargs(*args):
    print(sum(*args))
    return sum(*args)
print(addargs(nums)) 

传值为列表的时候:

import math
def multi_values(*args):  
    print(args)
    print(sum(args))
list_data=[1,2,3,4,5]
multi_values(*list_data)

输出:

(1, 2, 3, 4, 5)

15

注意:

1、如果是结构数据类型直接传入,需要在变量前加*号

2、无论传入什么,*args接收到数据后都会转元组

单星*args参数组(可变参数)和关键字参数混合

def multi_values(num,*args):
    print(num)
    print(args)

multi_values(1,2,3,4,5)         #调用函数多值传参

输出:
1
(2, 3, 4, 5)

def multi_values(num1,num2=100,*args):
    print(num1)
    print(num2)
    print(args)

multi_values(1,2,3,4,5) 

输出:

1
2
(3, 4, 5)

特别注意:

1、如果要对调num,*args位置,num需要使用关键字传参数,否则不支持位参

2、注意如果有默认参数存在,取值的逻辑与关键字一致

3、如果默认参数在*args之后,使用位置参数赋值,这个就取不到了,因为args吃了所有的数据。所以这种情况需要用关键字参数。

4、出现位参与关键字参数同时传参的时候,要以位参开始

拥有两星(**)的参数组(可变关键字参数)

#常规方法 (直接 命名关键字/可变关键字,abc就是关键字)
def multi_values(**kwargs): print(kwargs) multi_values(a=1,b=2,c=4) #调用函数多值传参

 输出:

{'a': 1, 'b': 2, 'c': 4}

#直接带入字典类型,使用**
dict_data={'a':1,'b':2}
def multi_kw(**kwargs):
    print(kwargs)
multi_kw(**dict_data)

1、双星号开始

2、双星号后面跟着一个关键字

3、kwargs是一个约定俗成的命名

4、kwargs你可以自定义(不建议)

5、调用时传键值一起传

6、**kwargs接收后转为字典

7、值打印或者return都是字典

特别注意:

1、**这里代表接收的可变关键字参数数量不受限制(大于等于0个)

2、**kwargs支持不传值,输出为空字典

3、kwargs=key word arguments代表关键字的参数(命名关键字)

全部参数混合搭配使用

特别注意:

1、如果可变参数和可变关键字参数同时出现,可变参数在前

2、可变关键字参数内key不能出现关键字参数现存名字

def school(name,location,*args,date_founded=2012,**kwargs):
    print("学校名字:",name)
    print("学校地址:",location)
    print("成立时间:",date_founded)
    print("学员名单:",args)
    print("课程与老师",kwargs)

school('万门大学','北京','mike','lucy','lily',正正='python基础',宁夫='Django')

输出如下:

学校名字: 万门大学
学校地址: 北京
成立时间: 2012
学员名单: ('mike', 'lucy', 'lily')
课程与老师 {'正正': 'python基础', '宁夫': 'Django'}

五、函数的其他要点之全局和局部变量

全局变量,通俗的说,在当前程序的任何位置都可以被访问的变量叫做全局变量。如下:

name='mike'
age=28
height=1.72
list_data=['Python','Django']
dict_data={'name':'mike','age':28} 

如何把全局变量引入函数中使用,使用global关键字

discount_rate=0.9
def price_calculator(price):
    global discount_rate                                   #使用global关键字声明使用全局变量
    price_paid=price*discount_rate                         #price_paid是局部变量
    return price_paid
price_calculator(price=100)

输出:

90.0
dict_data={1:1,2:2}
int_data=100   #不可变变量
def g_func():
    # global dict_data
    #dict_data={}
    dict_data[1]=123
    print("函数内:",dict_data)
    int_data=100
    # global int_data  #不可变 修改需要 声明
    print("函数内:",int_data)
g_func()
print("全局变量dict->",dict_data,'全局变量int->',int_data,sep=' ')
输出:
函数内: {1: 123, 2: 2}
函数内: 100
全局变量dict-> {1: 123, 2: 2} 全局变量int-> 100

局部变量,通俗的说,在函数体内定义的变量叫做局部变量,只有在当前函数内才可以被访问

def add(num1,num2):
    result=num1+num2    #这里result就是局部变量
    return result
add(1,2)

六、函数的其他要点之return的数据类型

单一数据return

def add(num1,num2):
    result=num1+num2
    return result

add(0,10)

输出: 10

单一数据返回当前数据原始类型,比如result是int,那么return也是int 

多个数据return

def add(num1,num2):
    result=num1+num2
    return num1,num2,result

add(0,10)     #函数调用

 输出:(0, 10, 10)

多个数据返回全部会转为元组

七、函数对可变对象和不可变对象的操作

不可变类型:引用

#不可变对象
name='mike'
new_name=name    #引用
print(name)
print(id(name))
print(new_name)
print(id(new_name))
new_name=3.14         #当赋值时,引用关系解除
print(name)
print(new_name)
print(type(name))
print(type(new_name))
输出:
mike
140314274181232
mike
140314274181232
mike
3.14
<class 'str'>
<class 'float'>

如果当前对象无引用,对象则会被Python解释器释放

# 不可变类型
num=123
def change(num):
    num=1000
    return num
num=123
new=change(num=num)
print(num)
print(new)

输出:
123
1000

不可变类型在函数哪里都无法改变值 

可变类型

list_data=[1,2,3]
print(list_data)
list_data[0]=100
print(list_data[0])

 特别注意:

可变对象是对象引用对象,结构类型内部数据相对独立 

# 可变类型
num_list=[1,2,3]
def change(num):
    num[0]=1000
    return num
new=change(num=num_list)
print(num_list[0])
print(id(num_list))
print(id(new))

输出:

1000

140314260029248

140314260029248

八、函数调用函数

#函数调用函数
def max_age_cal(age):
    return max(age)
def min_age_cal(age):
    return min(age)

def age_cal(*args):
    print(args)
    max_age=max_age_cal(args)
    min_age=min_age_cal(args)
    print(max_age)
    print(min_age)
    return max_age,min_age

age_cal(20,25,30,33,28)

 输出:
(20, 25, 30, 33, 28)
33
20
[73]:
(33, 20)

#传参的方法
#函数调用函数 def max_age_cal(age): return max(age) def min_age_cal(age): return min(age) #引用对象 max_a=max_age_cal #注意这里没有括弧,有括弧就给直接调用了 min_a=min_age_cal def age_cal(*args): print(args) max_age=max_a(args) min_age=min_a(args) print(max_age) print(min_age) return max_age,min_age age_cal(20,25,30,33,28)

 输出:
(20, 25, 30, 33, 28)
33
20
[73]:
(33, 20)  

附:一个函数的说明:可以使用help()打印函数说明。 

help(print) 

 输出:

筛选录取输出函数实例:

pass_score_list={}
pass_list=[]
# *args是老师,**kwargs是学生列表
def enroll(university,major,pass_score=550,pass_number=10,*args,**kwargs):
    global pass_score_list,pass_list
    if len(kwargs)>0:
        for stu,score in kwargs.items():
            if int(score)>pass_score:
                pass_score_list[stu]=int(score)
        desc_sort=sorted(pass_score_list.items(), key=lambda k:k[1],reverse=True)
        print("***演示输出排序输出***",desc_sort,'\n')
        
        for stu in desc_sort[:pass_number]:
            pass_list.append(stu)
        print(f"学校:{university};\n专业:{major}\n录取分数线:{pass_score}\n录取人数:{pass_number}\n招生老师名单:{args}")
    else:
        print("无人报考")
enroll('西北工业大学','计算机专业',550,3,'张老师','王老师','李老师',张旭='540',李阳='575',王强='583',徐增='569',齐凯='557')

 九、函数的最小作战单元--------匿名函数lambda

lambda是一个匿名函数,也是一个表达式,匿名函数顾名思义,就是"无名函数",匿名函数使用简单的业务逻辑,一行一个函数。逻辑不复杂,不需要大规模批量调用的功能,可使用匿名函数构建,匿名函数的书写特性决定其简易性。lambda是Python的一个关键字,用于声明一个匿名函数。

  常规函数 匿名函数
命名 必须 无须
多参数 支持 支持
功能 复杂/多行 简单/单行/表达式
作为参数传递 支持 支持

传统函数

def add(num1):
  return num1*2
print(add(10))     #输出20

lambda函数

result=lambda x:x*2
print(result(10))       #输出20

 三目运算符

传统判断模式:

a=5
b=3
if a>b:
    print('a大于b')
else:
    print('a小于b')

 三目运算符判断模式:

传统函数

a=5
b=3
result='a大于b' if a>b else 'a小于b'
print(result)

 匿名函数

x=10
y=20
compare=lambda x,y:x if x>y else y
print(compare(x,y))

 匿名函数的使用方式 

1、把lambda作为一个方法传入传统函数,常规带入模式

schools=['Python基础','Python爬虫','Java编程','Java web','Python数据分析']
def search_python(list,func):
    result=[]
    for school in list:
        if func(school):
            result.append(school)
    return result
condition=lambda x:True if 'Python' in x else False
search_python(schools,condition)

 输出:

['Python基础', 'Python爬虫', 'Python数据分析'] 

2、给def函数中的lambda传参数

def main():
    return lambda x:True if x==10 else False
# 传参
# 执行main函数,main函数不需要参数,第二个括号为lambda传送x的参数
main()(10)

 输出:True

3、把lambda作为一个方法传入传统函数,函数生成函数模式

schools=['Python基础','Python爬虫','Java编程','Java web','Python数据分析']
# 函数生成函数模式
def income(basic,transport,phone):
    return lambda x:x+basic+transport+phone
total=income(10000,5000,200)    
print(total(3500))

 输出:18700

schools=['Python基础','Python爬虫','Java编程','Java web','Python数据分析']
def keyword_search(keys,func):
    search_result=[]
    for result in keys:
        if func(result):
            search_result.append(result)
    return search_result
def search_condition(c):
    return lambda x:True if c in x else False

python=search_condition("Python")
java=search_condition("Java")
result_python=keyword_search(schools,python)
print(result_python)
result_java=keyword_search(schools,java)
print(result_java)

  输出:

['Python基础', 'Python爬虫', 'Python数据分析']

['Java编程', 'Java web']

匿名函数组合使用

map、filter、reduce、sorted,这四个是Python的常用的四个函数,但是这四个函数有几个共同点

1、每个函数都有自己的特定功能

2、每个函数都需要依赖一种"规则"来实现自己的特定的功能

3、规则可用“表达式”或者函数来实现

什么是表达式?

将同类型的数据(如常量、变量、函数等),用运算符号按照一定的规则串联起来的、有意义的式子成为表达式(a>b,a==b,(.*?),1+1)

map函数

map(function,sequense)

 function,可以使(def,lambda方法),sequence是可迭代序列  

结果生成一个新的序列

传统函数方式:

data=[1,2,3,4,5]
new_list=[]
for n in data:
    new_list.append(n+1)
print(new_list)

 输出结果:[2, 3, 4, 5, 6]

传统函数加map:

data=[1,2,3,4,5]
def func(n):
    return n+1
new_list=map(func,data)
new_list=list(new_list)
print(new_list)

 输出结果:[2, 3, 4, 5, 6]

lambda表达式加map:

data=[1,2,3,4,5]
new_list=list(map(lambda n:n+1,data))
print(new_list)

 输出结果:[2, 3, 4, 5, 6]

特别注意:

1、参数顺序------map(方法/表达式,可迭代对象)

2、map返回的结果是一个迭代器,需要list转一下即可调用

3、放入传统函数不要(),map已经知道函数实体,它会去执行

filter函数

 

filter(function,sequence)

 

 function,可以使(def,lambda方法),sequence是可迭代序列   

结果生成、返回一个新的序列 

data=[1,2,3,4,5]
new_list=list(filter(lambda x:x>2,data))
print(new_list)

特别注意:

1、filter需要函数返回布尔类型作为筛选判断

2、filter返回的结果是一个迭代器,需要list转一下即可调用  

reduce函数

reduce(function,sequence)

 function,可以使(def,lambda方法),sequence是可迭代序列  

 特别注意:
1、reduce每次会从可迭代对象中抽取两个元素进行运算(运算规则基于方法)
2、可以看到reduce这里的本意就是一次一次减少可计算的元素直到尽头
 
from functools import reduce
data=[1,2,3,4,5]
res=reduce(lambda x,y:x+y,data)
print(res)

 输出结果:15

特别注意:reduce需要从functools库中引入

sorted函数

sorted(iterable,key=None,reverse=False)

1、iterable是可迭代的类型数据(list列表,dict字典)

2、key默认参数,默认值为None。key是基于可迭代对象中某一个对象进行排序,需要函数/lambda进行规则定义

3、reverse默认参数默认值为False。reverse是排序"方向",false为升序,True为降序。

由此可见,我们不需要给key和reverse传参函数也可以执行。iterable需要传参 

data=[9,6,3,1]
new_data=sorted(data)
print(new_data)

输出结果:
[1, 3, 6, 9]

根据列表中元素出现次数进行排序

unsorted=[1,2,9,8,6,4,3,3,2,4,4,4,3,5,5,6,6,5,6,5,7,7,7]
map={}
for number in unsorted:
    if number in map:
        map[number]=map[number]+1
    else:
        map[number]=1
sort=sorted(map.items(),key=lambda item:item[1],reverse=True)
print(sort)

根据学生分数高低进行排序

exam_result={'张旭':'540','李阳':'575','王强':'583','徐增':'569','齐飞':'557'}
des_order=sorted(exam_result.items(),key=lambda x:x[1],reverse=True)
print(des_order)

注意:

1、排序结果不会影响原始字符

2、返回新的排序记过为列表嵌套元组,可用dict()转回字典

排序任务-使用列表内的列表的第一个元素升序排序

L_data=[[3,4,1],[3,3,3],[1,2,4],[9,1,0],[7,3,2]]
asc_order1=sorted(L_data,key=lambda d:d[0],reverse=False)
print(asc_order1)
asc_order2=sorted(L_data,key=lambda d:(d[0],d[1]),reverse=False)
print(asc_order2)

sorted函数返回值,修改内部值对原始值的影响

1、第一种情况 可以对原始值进行写入

#第一种情况 可以对原始值进行写入!
data1=[{'age':16,'2':30},{'age':15,'4':10}]
def test1(data):
    s=sorted(data,key=lambda x:x['age'])
    s[0]['age']=100
test1(data1)
print(data1)

2、列表中的列表数据,也可以改变其原始的值

#第二种情况 可以对原始值进行写入!
data2=[[1,2,3],[4,5,6]]
def test2(data):
    s=sorted(data,key=lambda x:x[0],reverse=True)
    s[0][0]=100
test2(data2)
print(data2) 

3、不可以对原始值进行写入,sorted后的值与原值独立

#第三种情况 不可以对原始值进行写入,sorted后的值与原值独立
data3=[3,2,1,2,5,3]
def test3(data):
    s=sorted(data)
    s[1]=123
    return s
print(test3(data3))
print(data3)

如果只有一级数据[1,123,2,3,3,5],这个就直接属于sorted,直接创建了对象。

如果有二级数据[[1,2,3],[100,5,6]],这里的第一级还是sorted的对象,但是内部的数据是引用的原数据,所以你才可以修改

匿名函数函数嵌套

def sum(x,y):
    print("x与y的和为:",x+y)
    return lambda a,b:a+b
                 #(参数) #(逻辑运算)
f=sum(8,9)
print(f(1,2))
#特别注意  匿名函数也是函数,换句话说在传统函数里加入匿名函数,形成了闭包即函数的嵌套
#逻辑运算部分值可以直接带入,但是参数部分需要传入

 类比传统def函数

#类比一下 传统def
def outer(n,x,y):
    print(n,x,y) #观察n
    def inner(a,b):
        return a+b+n
    return inner
main_func=outer(1,2,3)    #调用外层函数
main_func(8,10)           #调用内层inner函数

十、Python读取Excel文件内容 

Python无法打开.xlsx文件:xlrd.biffh.XLRDError: Excel xlsx file; not supported

原因是Python中xlrd模块 2.0.1 版本不支持.xlsx

回退Python版本至1.2.0版本

在桌面创建一个excel文件,excel结尾为xls

文件内容为:

 读取Excel表格的代码如下:

#任务说明
#我们做一个城市排行榜,从以上排行榜选取10个城市,通过不同维度的评价标准
#统计出每个维度下城市的排名,然后再根据排名的结果得出不同维度下的分数,然后算出每个城市的总分

#评价数据:
#1、选取10个城市
#2、查询每个城市的以下8项公开数据(如果2019年的数据查询不到,也可以用2018的数据代替)
#****数据维度****
#(1)019年GDP
#(2)人口数量
#(3)平均房价
#(4)平均工资
#(5)地铁总里程
#(6)高校数量
#(7)三甲医院数量
#(8)小学生数量

#评价维度:
#1、首先按照原榜单给这10个城市打分,并作为新字段写入字典,排名第一得10分,排名最后得1分,以此类推
#2、计算每个城市工资和房价的比值,精确到两位小数,然后将这个数据添加到城市数据中。
#   截止目前,连上原来的8条数据,每个城市共有10条数据。
#3、将每个城市按照10条数据分别进行排名,除房价外,排名第一得10分,排名最后得1分
#   以此类推,房价排名第一得1分,排名最后得10分,然后将每个城市的10项数据得分相加
#   得出城市的总得分,满分一百分,最后打印每个评价维度下每个城市的得分排名情况以及最终总分排名

#整体读取思路
#1、从Excel中读取数据
#2、放到字典中
#3、再把字典放到列表中
import xlrd

#以下两行代码操作跟我们常规在电脑打开excel的流程一致
book=xlrd.open_workbook('C:\\Users\\vitue\\Desktop\\city_data.xls')  #双击打开excel文件(workbook工作簿)
sheet=book.sheet_by_index(0)   #点击打开需要编辑的sheet(sheet工作表)

#再往下是操作具体的单元格cell,也就是编辑单元格数据(增删改查)
main_data_list=[] #先建一个空list,等待放入所有的dict

                   #总行数
for row in range(2,sheet.nrows):  #观察表格我们发现我们需要的数据从第4行开始
    temp_dict={}    #准备好每条数据需要使用的dict 以行为单位,一行一个dict
    print(sheet.row_values(row))   #一行数据作为列表返回
    #列表基于下标index取值与关键字key做映射(mapping)
    #注意这里的key名,和原始的excel有差异,这里涉及到数据的字段名对齐
    temp_dict['城市']=sheet.row_values(row)[0]
    temp_dict['2019GDP']=sheet.row_values(row)[1]
    temp_dict['2018总人口']=sheet.row_values(row)[2]
    temp_dict['2018平均房价']=sheet.row_values(row)[3]
    temp_dict['2018平均工资']=sheet.row_values(row)[4]
    temp_dict['地铁总里程']=sheet.row_values(row)[5]
    temp_dict['高校数量']=sheet.row_values(row)[6]
    temp_dict['三甲医院数量']=sheet.row_values(row)[7]
    temp_dict['2018小学生数量']=sheet.row_values(row)[8]

    main_data_list.append(temp_dict)
print(main_data_list)
#按照原来榜单的排名顺序给每个城市打分
#排名第一得10分,排名最后得1分
#将得分作为**新数据/字段**添加进城市字典(作为一个数据考量维度)
for i_list in range(len(main_data_list)):
    # 穿透的列表 然后穿透到 字典进行操作    #注意这里 列表长度-当前的下标=得分
    main_data_list[i_list]['原榜单得分']=len(main_data_list)-i_list

#打印每个城市在原榜单评选中的得分
print('城市原榜单得分(从高到低):')
for j_list in range(len(main_data_list)):
    print(main_data_list[j_list]['城市'],main_data_list[j_list]['原榜单得分'])
print('-'*80) #黄金分割线,甩出一条出来
#计算每个城市的收入房价比,精确2位小数,然后添加到字典中作为**新数据/字段**
for i in range(len(main_data_list)):
                            #注意直接使用format(3.1415,'.2f')和占位'{:2.f}'.format()的区别
    main_data_list[i]['收入房价比']=format(main_data_list[i]['2018平均工资']/12/main_data_list[i]['2018平均房价'],'.2f')
print(main_data_list)

#按照不同的标准对列表中的字典进行排序,注意房价跟其他标准打分规则不一样
#房价为啥打分规则不一样?因为 常规排名是越多越好,

#定义一个从字典中取'k'这个key的值的函数
def get_value(dic,k):
    return dic.get(k)  #回顾一下dic.get和dict[key]取值二者有什么区别?

#做一个 可高度复用的函数,且函数有较高的宽容度(兼容升降两种规则)

def desc_cities(cities_list,kw='2019GDP'):
    if kw!='2018平价房价':#判断评选标准是不是房价,因为房价和其他标准得分规则不一样

            #------------------------------先排序----------------------------
        #按照kw对列表中的字典进行排序 可迭代对象     调用函数取key对应的value做比较  注意100-0
        cities_bykey=sorted(cities_list,key=lambda x:get_value(x,kw),reverse=True)
        
            #-----------------------------再打分-------------------------------
        #按照kw的排名顺序给每个城市打分,排名第一得10分,排名最后得1分
        #以此类推,然后将得分作为新数据添加进城市字典
        
        for i_key in range(len(cities_bykey)):  
            cities_bykey[i_key][kw+'得分']=len(cities_bykey)-i_key
                                          #len=10 i=0 10-0=10 最高分
        #打印每个城市在key评选中的得分
        print(f'城市{kw}得分(由高到低):')
        for j_key in range(len(cities_bykey)):
            print(cities_bykey[j_key]['城市'],cities_bykey[j_key][kw+'得分'])
        print('-'*80)
    else:#如果评分标准是房价的话
        #按照‘2018平均房价’对列表中的字典进行排序
        cities_bykey=sorted(cities_list,key=lambda x:get_value(x,kw),reverse=True)
        #按照'2018平均房价'的排名顺序给每个城市打分,排名第一得1分,排名最后得10分
        #以此类推,然后将得分作为新数据添加进城市字典
        #特别注意! cities_list本质还是列表,基于字典的一个值排序,那么返回值还是list
        #特别注意! 如果cities_list本质是字典即最外层就是字典,那么返回的是[(),()]
        for i_house in range(len(cities_bykey)):
            cities_bykey[i_house][kw+'得分']=i_house+1  #注意这个逻辑0+1最高的分数最低

        #打印每个城市在'2018平均房价'评选中的得分
        print(f'城市{kw}得分{由低到高}:')

        for j_house in range(len(cities_bykey)):
            print(cities_bykey[j_house]['城市'],cities_bykey[j_house][kw+'得分'])
        print('-'*80)

    return cities_bykey

# *******批量调用函数 进行排名+单项得分计算 ******
desc_cities(main_data_list,'2019GDP')
desc_cities(main_data_list,'2018总人口')
desc_cities(main_data_list,'2018平均房价')
desc_cities(main_data_list,'2018平均工资')
desc_cities(main_data_list,'地铁总里程')
desc_cities(main_data_list,'高校数量')
desc_cities(main_data_list,'三甲医院数量')
desc_cities(main_data_list,'2018小学生数量')
cities_bykey=desc_cities(main_data_list,'收入房价比')

#********总结性数据处理 ******,这个是收尾的部分,也是成果部分的输出*******

#计算城市总得分,然后将总得分作为新数据添加进城市字典
for i_city in range(len(cities_bykey)):
    #把每个维度的得分相加! 再追加一个数据(key value进去) 城市总得分
    cities_bykey[i_city]['城市总得分']=cities_bykey[i_city]['原榜单得分']+cities_bykey[i_city]['2019GDP得分']+cities_bykey[i_city]['2018总人口得分']+cities_bykey[i_city]['2018平均房价得分']+cities_bykey[i_city]['2018平均工资得分']+cities_bykey[i_city]['地铁总里程得分']+cities_bykey[i_city]['高校数量得分']+cities_bykey[i_city]['三甲医院数量得分']+cities_bykey[i_city]['2018小学生数量得分']+cities_bykey[i_city]['收入房价比得分']

#为了整体输出完整,原排行榜分数再打印一次
print('城市原榜单得分(由高到低):')
for j_list in range(len(main_data_list)):
    print(main_data_list[j_list]['城市'],main_data_list[j_list]['原榜单得分'])

#按照总得分对列表中的字典进行降序排序
cities_byscore=sorted(cities_bykey,key=lambda x:get_value(x,'城市总得分'),reverse=True)
#打印每个城市的总得分排名
print(f'城市总得分(由高到低):')
for j_score in range(len(cities_byscore)):
    print(cities_byscore[j_score]['城市'],cities_byscore[j_score]['城市总得分'])

 输出结果:

十一、函数装饰器decorator

装饰器是一个可以起到装饰作用的工具,装饰器本质还是一个函数,是一个嵌套的函数(闭包),装饰器装饰的还是函数。装饰器(函数)是对现有的函数功能的一个补充和增加,它可以更快、更清晰、大批量的为函数增加功能。装饰器的作用是为了让代码更简单易懂,在不变更原有函数代码的情况下对现有函数功能进行增强。

什么是闭包?

  • 必须有一个嵌套函数(一个函数在另一个函数中)
  • 这个嵌套(内函数)必须引用外部函数的变量
  • 封闭函数必须返回嵌套函数

装饰器闭包函数举例:

def outer_func(x,y):
    print(x+y)
    def inner_func(a,b):
        print(x+a+b)
        return x+a+b
    return inner_func  
def outer_func(x):
    print(x)     #outer的局部变量 
    def inner_func(y): #inner形参
        print("I am inner func")
        return x+y   #x引用outer的变量
    return inner_func #outer返回inner函数(注意非功能)
test1=outer_func(1) #注意这里调用了outer,但是未执行,因为是没有()
#因为需要inner在函数外部执行时再去引用x,如果执行了,整个函数就结束了 test1(10)
def outer_func1():
    message='hi 万门'
    def inner_func1():
        print(message)
    return inner_func1()
outer_func1()

def outer_func2():
    message='hi 万门'
    def inner_func2():
        print(message)
    return inner_func2           #函数的待命状态,不执行
my_func2=outer_func2()
my_func2()

输出:

hi 万门
hi 万门
def outer_func(msg):
    message=msg
    def inner_func():
        print(message)
    return inner_func

hello_func=outer_func('hello')
python_func=outer_func('Python')

hello_func()
python_func()
 这里return的是一个函数名字(实体),并没有(),换言之,他不会执行,处于待命状态 
不仅仅是变量可以作为参数传入函数,函数也可以作为参数传入函数
def deco_func(original_func):
    def wrapper_func():
        return original_func()
    return wrapper_func
def show_age():
    print("I am 29")
#先把show age带入函数
deco_show_age=deco_func(show_age)
#调用内部函数 deco_show_age()

声明和调用一个装饰器

1、先创建装饰器

2、用装饰器去"装饰" 

#创建装饰器逻辑
def deco_func(orginal_func):
    def wrapper_func():
        return orginal_func()
    return wrapper_func
#使用装饰器去装饰 @+装饰器的名字(函数名字)
@deco_func
def show_age():
    print("I am 29")

show_age()

特别注意!->装饰器装饰的函数,会先执行装饰器"函数",在执行被装饰函数。装饰器没有改变原有调用/执行的方式。

原始函数带参数,提升装饰器的宽容度

def deco_func(orginal_func):
    def wrapper_func(*args,**kwargs):
        return orginal_func(*args,**kwargs)
    return wrapper_func

@deco_func                           #一个装饰器宽容多个函数
def show_age():
    print("I am 29")

show_age()

@deco_func                           #一个装饰器宽容多个函数
def show_info(name,age):
    print(name,age)

show_info('kk',29)

@deco_func                           #一个装饰器宽容多个函数
def show_more(**kwargs):
    print(kwargs)

show_more(school='万门',coding='Python')

输出结果:

I am 29

kk 29

{'school': '万门', 'coding': 'Python'}

装饰器传入参数,提高装饰器的变通性、宽容度

def deco_teacher(teacher_name):
    def school_teacher(original_func):
        def wrapper_func(msg):
            return original_func(msg+"老师是"+teacher_name)
        return wrapper_func
    return school_teacher
    
@deco_teacher('硕硕')
def stu1(name):
    print("我叫",name)
stu1("mike")

@deco_teacher('正正')
def stu2(name):
    print("我叫",name)
stu2('susan')

@deco_teacher('陪陪')
def stu3(name):
    print("我叫",name)
stu3('nico')

输出结果:

我叫 mike老师是硕硕

我叫 susan老师是正正

我叫 nico老师是陪陪

装饰器举例1:计时器

import time
def timer(func):
    def wrapper():
        start=time.time()
        func()
        end=time.time()
        return end-start
    return wrapper

@timer
def consumer1():
    l_data=[]
    for x in range(100):
        l_data:append(x)

@timer
def consumer2():
    time.sleep(2)

consumer1()
consumer2()

输出结果:

装饰器举例2:登录系统

def login_required(password):
    def wrapper():
        password=input("输入密码:")
        if password=="123":
            print('密码正确,登陆成功')
        else:
            print('密码错误')
    return wrapper
def admin_1():
    print("您已经进入1号管理后台")

@login_required
def admin_2():
    print("您已经进入2号管理后台")

@login_required
def admin_3():
    print("您已经进入3号管理后台")

admin_1()
admin_2()
admin_3()

输出结果:

装饰器溯源问题

装饰器本身就是一个函数,那么装饰器去装饰另外一个函数的时候,原有的函数就会"消逝",为了避免让这个最头部的函数(被装饰的函数)不消失,可以使用wraps函数,他的作用是保证被修饰的函数名不能改变,注意wraps函数是追溯上一函数名字,所以如果有多个装饰器,那么每个装饰器都要有,这条线不能断

原始无法溯源的代码

#这是一个优于装饰器自我迷失的函数
def a(func):
    def wrap():
        print('123')
        print(wrap.__name__)
        return func()
    return wrap

def b(func):
    def wrap():
        print('321')
        print(wrap.__name__)
        return func()
    return wrap

@a
@b
def xx():
    print('abc')
xx()

输出:

123
wrap
321
wrap
abc

使用@wrap更改后可溯源

from functools import wraps
#这是一个找回自我版本!
def a(func):
    @wraps(func)
    def wrap():
        print('123')
        print(wrap.__name__)
        return func()
    return wrap

def b(func):
    @wraps(func)
    def wrap():
        print('321')
        print(wrap.__name__)
        return func()
    return wrap

@a
@b
def xx():
    print('abc')
xx()

输出结果为:

123
xx
321
xx
abc

 经销系统

# 用装饰器改进进销存系统
# 模拟一个简单的进销存软件,要用到装饰器(需要考虑哪些功能适合使用装饰器)
# 1、操作之前需要通过*账户秘密*验证是否登陆成功
# 2、每次登陆需要将登陆的用户名、登陆时间写入*日志*(日志用某个数据类型记录即可)
# 3、登陆成功后可以增加商品名称以及对应的库存,增加商品时,需要判断你的商品不能是*违禁商品* (自定义违禁商品)
# 4、当有商品入库或者销售出去的时候,需要改变对应*库存*
# 5、可以查询某商品现有库存(注意这里的数据结构)

#*** 先对我们的全局变量进行布局***

#定义一个全局变量,控制登陆状态 bool
login_status=False

#定义一个全局变量保存日志list
log=[]

#定义一个保存违禁品的list
prohibit_list=['枪支','弹药','药品','野生动物']

#定义一个全局变量保存管理员用户名和密码
super_adm={'user':'peipei','pwd':'123456'}

#定义一个全局变量保存添加的商品
product={}
print(login_status,log,prohibit_list,super_adm,product,sep='\n')

import time
from functools import wraps

#****************特别注意,你在使用装饰器的时候逻辑顺序***************
#比如 登陆肯定在日志前,没有登陆成功就没有操作日志,

#登陆功能   *装饰器*
def login(func):
                     #注意这里的参数,因为验证某个函数功能是否需要登陆时,被修饰的函数的参数不确定,而且login也不需要这些参数
    @wraps(func)
    def wrapper_func(*args,**kwargs):
        global login_status,super_adm
        #判断是否有登陆
        if login_status==True:
            return func(*args,**kwargs)
        else:
            #无登录 要求输入用户名和密码,如果判断到这里证明了用户是第一次使用
            login_user=input("请输入用户名:")
            login_pwd=input("请输入密码:")
            if login_user==super_adm['user'] and login_pwd==super_adm['pwd']:
                login_status=True
                return func(*args,**kwargs)
            else:
                print('登陆失败,账户名或密码或身份错误')
    return wrapper_func

#操作时间和操作用户的日志 *装饰器*
def login_write(func):
    @wraps(func)
    def wrapper_func(*args,**kwargs):
        global log,super_adm
        log_dict={} #建立一个临时的字典,存储用户操作行为和信息
        log_dict['用户名']=super_adm['user']
        log_dict['登陆时间']=time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
        log_dict['操作类型']=wrapper_func.__name__
        log_dict['操作内容']=args
        log.append(log_dict)  #向全局log添加这个临时字典 log_dic
        return func(*args,**kwargs)
    return wrapper_func

#判断是否有违禁物品 *装饰器*
def is_prohibit(func):
    @wraps(func)
    def wrapper_func(product_name,product_stock_name):
        global prohibit_status
        if product_name in prohibit_list:
            print("您的商品属于违禁商品!禁止添加!")
        else:
            return func(product_name,product_stock_name)
    return wrapper_func
    
#增加商品并入库
#login 
@login_write
@is_prohibit
def add_product_stock(product_name,product_stock_num):
    global product
    if product_name in product.keys():
        product[product_name]=product[product_name]+product_stock_num
    else:
        product[product_name]=product_stock_num

#销售某商品
# @login
@login_write
def sale_product(product_name,product_sale_num):
    global product
    if product_name not in product.keys():
        print('您销售的商品不存在')
    elif product[product_name]<product_sale_num:
        print('对不起!库存不足!')
    else:
        #销售后的库存更新 减除
        product[product_name]=product[product_name]-product_sale_num

#查询某商品库存
# @login
@login_write
def query_product(product_name):
    if product_name not in product.keys():
        print('您销售的商品不存在')
    else:
        product_stock=product[product_name]
        print(product_name,'的库存是:',product_stock)

#主函数 main 函数
@login  #注意主程序开始之前,先查看用户的登陆状态
def main():
    #函数调用的新玩儿法,因为python没有Switch case语句,即多选,所以我们用字典搞定
    functions={
        '1':add_product_stock,
        '2':sale_product,
        '3':query_product,
    }

    func_choice=input('请输入功能编号------:')

    if func_choice=='1':
        #注意这里的调用关系,func.get(key)获得了值,但是这个值是一个对象,是一个函数那么再加()就执行了该函数
        product_name=input('请输入入库产品名称:')
        stock_num=int(input('请输入入库产品数量:'))
        functions.get(func_choice)(product_name,stock_num)

    elif func_choice=='2':
        product_name=input('请输入销售产品名称:')
        product_sale_num=int(input('请输入销售产品数量:'))
        functions.get(func_choice)(product_name,product_sale_num)

    elif func_choice=='3':
        product_name=input('请输入销售产品名称:')
        functions.get(func_choice)(product_name)

#   __name__=='__main__'这个部分是一个关于python解释器需要判断的一个东西
#  当前
if  __name__=='__main__':
    print("************欢迎来到万门大学Python ERP演示系统************")
    print("************请输入功能编号进行操作***********")
    print("输入1 进行商品添加入库操作","输入2 销售减库操作","输入3库存查询操作",sep='\n')
    main()

 

posted @ 2024-04-09 17:16  leagueandlegends  阅读(38)  评论(0)    收藏  举报