python整理总结(一)

python2,3的区别

1.Python2中使用 ASCII 码作为默认编码方式导致string有两种类型str和unicode,Python3只支持unicode的string。
# python2和python3字节和字符对应关系为:
python2    python3    表现    转换    作用
str         bytes     字节   encode  存储
unicode     str       字符   decode  显示
2. Python3采用的是绝对路径的方式进行import。- Python2中相对路径的import会导致标准库导入变得困难。


3.Python2中存在老式类和新式类的区别,Python3统一采用新式类。新式类声明要求继承object,必须用新式类应用多重继承。

4.Python3使用更加严格的缩进。Python2的缩进机制中,1个tab和8个space是等价的,所以在缩进中可以同时允许tab和space在代码中共存。

-print语句被python3废弃,统一使用print函数

-exec语句被python3废弃,统一使用exec函数

-不相等操作符"<>"被Python3废弃,统一使用"!="

-long整数类型被Python3废弃,统一使用int

-xrange函数被Python3废弃,统一使用range,Python3中range的机制也进行修改并提高了大数据集生成效率

-Python3中这些方法再不再返回list对象:dictionary关联的keys()、values()、items(),zip(),map(),filter(),但是可以通过list强行转换:
-迭代器iterator的next()函数被Python3废弃,统一使用next(iterator)

-raw_input函数被Python3废弃,统一使用input函数

-字典变量的has_key函数被Python废弃,统一使用in关键词

-file函数被Python3废弃,统一使用open来处理文件,可以通过io.IOBase检查文件类型

-异常StandardError 被Python3废弃,统一使用Exception

-浮点数除法操作符/和//区别

Python2:/是整数除法,//是小数除法
Python3:/是小数除法,//是整数除法。

- round函数返回值区别
    Python2,round函数返回float类型值
    Python3,round函数返回int类型值
 
-比较操作符区别
    Python2中任意两个对象都可以比较
    Python3中只有同一数据类型的对象可以比较

变量:

把程序运行的中间结果临时的存在内存里,以便后续的代码调用。

变量的定义规则:

1、变量只能由 数字,字母,下划线任意组合。
2、不能以数字开头。
3、不能是python中的关键字。

1
['and''as''assert''break''class''continue''def''del''elif''else''except''exec''finally''for''from''global''if''import''in''is''lambda''not''or''pass''print''raise''return''try''while''with''yield']

 

4、变量要具有可描述性。
   name = 'alex'
   age = 73 
5、变量不能使用中文。
6、变量不宜过长。

变量的命名:

 

复制代码
#驼峰体

AgeOfOldboy = 56

NumberOfStudents = 80

#下划线

age_of_oldboy = 56

number_of_students = 80
复制代码

 

常量:

指不变的量,常量的设置:全部大写的变量,就是常量。

注释

单行注释用;#被注释内容

多行注释用;'''被注释内容'''

 那么都在哪里应该添加注释呢?

例子:

  1. 关键节点:或者一些难以理解的代码加注释;
  2. 文件:文件的描述
  3. 函数:函数的描述 
  4. 类:类的描述

 

数据类型及操作方法:

字符串:

字符串拼接

数字可以进行加减乘除等运算,字符串呢?让我大声告诉你,也能?what ?是的,但只能进行"相加""相乘"运算。

复制代码
>>> name
'Alex Li'
>>> age
'22'
>>> 
>>> name + age  #相加其实就是简单拼接
'Alex Li22'
>>> 
>>> name * 10 #相乘其实就是复制自己多少次,再拼接在一起
'Alex LiAlex LiAlex LiAlex LiAlex LiAlex LiAlex LiAlex LiAlex LiAlex Li'
复制代码
注意,字符串的拼接只能是双方都是字符串,不能跟数字或其它类型拼接


>>> type(name),type(age2)
(<type 'str'>, <type 'int'>)
>>> 
>>> name
'Alex Li'
>>> age2
22
>>> name + age2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects #错误提示数字 和 字符 不能拼接


字符串的格式化输出
01“旧式”字符串解析(%操作符)

02“新式”字符串格式化(str.format)

03  字符串插值/f-Strings(Python 3.6+)
 Python

>>> a = 5

>>> b = 10

>>> f'Five plus ten is {a + b} and not {2 * (a + b)}.'

04  字符串模板(Python标准库) 
Python

>>> from string import Template

>>> t = Template('Hey, $name!')

>>> t.substitute(name=name)

索引切片
s = 'python is best language ok'
# 用索引取值
print(s[-5])
print(s[10])
# 切片 0可以省略不写
print(s[0:10])
print(s[:10])
print(s[:])
print(s[:10:2]) # 加步长每隔2个取一个值
# 反向取值必须加步长, 最后一位的索引值为-1,
print(s[-1:-8])
print(s[-1:-8:-2])



内置方法
# 大前提:对字符串的任何操作都是产生一个新的字符串,与原字符串没有关系
s = 'Wua123jing666'
t1 = s.count('a') # 在字符串中查找指定字符有多少个
t2 = s.upper() # 将字符串中的字符全部大写
t3 = s.lower() # 将字符串中的字符全部小写
t4 = s.title() # 非字母隔开的每个单词的首字母大写
t5 = s.find('a') # 通过元素获取其索引,找到第一个就返回,找不到会返回-1。
t6 = s.index('a') # 通过元素获取其索引,找到第一个就返回,找不到会报错。
t7 = s.capitalize() # 将字符串中的首字母大写
t8 = s.replace('a','nb',2) # 替换,由什么替换成什么,替换几次
t9 = s.startswith('w') # 判断以什么为开头 可以切片
t10 = s.startswith('i',3,) # 判断以什么为开头 可以切片,取索引3后面的所有字符
t11 = s.endswith('i',3) # 判断以什么结尾,可以切片,取索引3后面的所有字符
t12 = s.swapcase() # 大小写反转
t13 = s.isupper() # 判断字符串中所有字母是否为大写,返回布尔值
t14 = s.islower() # 判断字符串中所有字母是否为小写,返回布尔值
t15 = s.isdigit() # 字符串只由数字组成
t16 = s.isalpha() # 字符串只由字母组成
t17 = s.isalnum() # 字符串由字母或数字组成
---------------------------------------------------------------
t18 = s.strip('W') # 默认去除字符串两边的空格,换行符,制表符,只是去除左边-->lstrip()去除右边-->rstrip(),可设置去除的字符
t19 = s.split() # 默认按照空格分割,
t20 = s.split('r') # 指定字符进行分割,
t21 = s.split('s',2) # 指定字符进行分割,且指定分割次数
---------------------------------------------------------------
join 连接符
l1 = ['wusir', 'alex', 'taibai'] # 操作列表时,列表里面的内容必须全部是字符串类型
t22 = ''.join(l1) # 默认连接
t23 = '-'.join(l1)   # 指定字符连接



列表:

增:

复制代码
s = ['alex','wusir','taibai']
# append 追加
s.append('henry')
# insert 插入
s.insert(1,'henry')
# extend 迭代追加
s.extend('henry')
s.extend(['oldboy','girl'])
复制代码

删:

复制代码
s = ['alex','wusir','taibai',10,11]
pop 按照索引删除,会将删除的元素作为返回值(默认删除最后一个)
s.pop(1)
remove 按照元素删除
s.remove('wusir')
clear 清空列表
s.clear()
del
按照索引删除
del s[1]
按照切片删除
del s[1:2]
删除整个列表
del s
复制代码

改:

s = ['alex','wusir','taibai',10,11]
切片改
s[:1] = 'asdf'
切片加步长改 必须一一对应
s[::2] = ['a','b','c']

查:

按照索引 按照切片 加步长查询
for 循环查询
s = ['alex','wusir','taibai',10,11]print(s)

列表的其他操作:

获取列表的长度              len()
计算某个元素出现的次数   conut
通过元素查找索引,找到第一个就返回,找不到就报错
从小到大排序sort(reverse = False) 当reverse = True时从大到小
反转用                        reverse
查找某个元素的索引用    index  找到第一个就返回,找不到报错

字典

字典是python中唯一的映射类型,采用键值对(key-value)的形式存储数据。python对key进行哈希函数运算,根据计算的结果决定value的存储地址,3.6版本之后字典是有序的(3.5版本之前字典是无序的),且key必须是可哈希的。可哈希表示key必须是不可变类型,如:数字、字符串、元组。

字典(dictionary)是除列表以外python之中最灵活的内置数据结构类型。列表是有序的对象结合,3.6版本之后字典是有序的对象集合。两者之间的区别在于:字典当中的元素是通过键来存取的,而不是通过偏移存取。

增:setdefault

 

复制代码
dic = {
    'name':'烟雨江南',
    'age':18,
    'sex':'man'
}
增
dic['hobby'] = '看书'  # 有则修改,无则添加
print(dic)
dic.setdefault('hobby','玩游戏') # 有则不变,无则添加
print(dic)
复制代码

 

删:pop、clear、popitem、del

复制代码
dic = {
    'name':'烟雨江南',
    'age':18,
    'sex':'man'
}
ret = dic.pop('name') # 按照键删除键值对,并返回删除的值
ret = dic.pop('name1','删除的对象不存在') # 设置两个参数,第二个参数是提示语,一般设置为None;键不存在也不会报错
print(ret)
dic.clear() # 清空字典
dic.popitem() #3.6版本之后删除最后一个键值对
print(dic)
del dic['name'] # 按照建删除
print(dic)
复制代码

改:update

复制代码
dic = {
    'name':'烟雨江南',
    'age':18,
    'sex':'man'
}
dic['name'] = 'jj'
print(dic)
dic.update(name = 'TSL',money=10000)  #更新:有则覆盖,没有则添加
print(dic)

dic = {"name": "jin", "age": 18, "sex": "male"}
dic2 = {"name": "alex", "weight": 75}
dic2.update(dic)
# 将dic里面的键值对覆盖添加到dic2中
复制代码

查:

复制代码
dic = {
    'name':'烟雨江南',
    'age':18,
    'sex':'man'
}
print(dic['age']) # 若不存在会报错

dic.get('age')    # 没有此键默认返回None
print(dic)
ret = dic.get('age1','此键不存在') # 键不存在,可设置提示语
print(ret)
复制代码

字典的其他操作:

复制代码

dic = {
'name': '太白金星',
'age': 18,
'hobby': 'wife',
}
# 查看键
res = dic.keys() # 查看字典中所有键
print(res,type(res)) # <class 'dict_keys'> 该类型可以循环遍历
for i in res:
print(i)
print(list(res)) # 也可以通过list转化为列表
-------------------------------------------------------------
# 查看值
res1 = dic.values() # 查看字典中所有值
print(res1,type(res1)) # <class 'dict_values'> 该类型可以循环遍历
for i in res1:
print(i)
print(list(res1)) # 也可以通过list转化为列表
-------------------------------------------------------------
#查看键值对
res2 = dic.items()
print(res2,type(res2)) # 查看字典中所有键值对
for i in res2:
print(i)
print(list(res2)) # 也可通过list转化为列表:[('name', '太白金星'), ('age', 18), ('hobby', 'wife')]
复制代码

集合

是一个无序的,不重复的数据组合,它的主要作用如下:

  • 去重,把一个列表变成集合,就自动去重了
  • 集合中的元素必须是不可变类型
  • 关系测试,测试两组数据之前的交集、差集、并集等关系

常用操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
= set([1,2,3,4,5])
= set([4,5,6,7,8])
 
print(a.intersection(b))  #交集  {4, 5}
print(a&b)
 
print(a.union(b))  #并集  {1, 2, 3, 4, 5, 6, 7, 8}
print(a|b)
 
print(a.difference(b)) #插集、得到的是a里有的b里没有的  {1, 2, 3}
print(a-b)
 
print(b.difference(a)) #插集、得到的是b里有的a里没有的  {8, 6, 7}
print(b-a)
 
print(a.symmetric_difference(b)) #方向交集、{1, 2, 3, 6, 7, 8}
print(a^b)

 

文件操作

文件操作:
#1. 打开文件的模式有(默认为文本模式):
r ,只读模式【默认模式,文件必须存在,不存在则抛出异常】
w,只写模式【不可读;不存在则创建;存在则清空内容】
a, 只追加写模式【不可读;不存在则创建;存在则只追加内容】

#2. 对于非文本文件,我们只能使用b模式,"b"表示以字节的方式操作(而所有文件也都是以字节的形式存储的,使用这种模式无需考虑文本文件的字符编码、图片文件的jgp格式、视频文件的avi格式)
rb
wb
ab
注:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型,不能指定编码

#3,‘+’模式(就是增加了一个功能)
r+, 读写【可读,可写】
w+,写读【可写,可读】
a+, 写读【可写,可读】

#4,以bytes类型操作的读写,写读,写读模式
r+b, 读写【可读,可写】
w+b,写读【可写,可读】
a+b, 写读【可写,可读】

r+ 读写模式:先读后写
rb 以字节的形式读取,带b的一般操作的都是非文字类的文件.
read:读取全部内容(文件较大时不可用,用for循环读取)
r模式:read(n)----n代表读取第几个字符
rb模式:read(n)---n代表读取第几个字节
其余的文件内光标移动都是以字节为单位的如:seek,tell,truncate
readline:读取第一行
readlines:返回一个list 列表的每个元素是源文件的每一行.
重点:seek光标移动(按字节移动)中文3个字节,英文一个字节,换行(\n)是2个字节;
seek(0,0)把光标移到起始位置
seek(0,1)当前光标所在位置
seek(0,2)把光标移到结束位置
r模式:read(n)----n代表读取第几个字符,只有r模式n代表字符,其他都是按字节读取


1. seek有三种移动方式0,1,2,其中1和2必须在b模式下进行,但无论哪种模式,都是以bytes为单位移动的
2. truncate是截断文件,所以文件的打开方式必须可写,但是不能用w或w+等方式打开,因为那样直接清空文件了,所以truncate要在r+或a或a+等模式下测试效果。
复制代码
常用操作:
def close(self, *args, **kwargs): # real signature unknown
        关闭文件
        pass

    def fileno(self, *args, **kwargs): # real signature unknown
        文件描述符
        pass

    def flush(self, *args, **kwargs): # real signature unknown
        刷新文件内部缓冲区
        pass

    def isatty(self, *args, **kwargs): # real signature unknown
        判断文件是否是同意tty设备
        pass

    def read(self, *args, **kwargs): # real signature unknown
        读取指定字节数据
        pass

    def readable(self, *args, **kwargs): # real signature unknown
        是否可读
        pass

    def readline(self, *args, **kwargs): # real signature unknown
        仅读取一行数据
        pass

    def seek(self, *args, **kwargs): # real signature unknown
        指定文件中指针位置
        pass

    def seekable(self, *args, **kwargs): # real signature unknown
        指针是否可操作
        pass

    def tell(self, *args, **kwargs): # real signature unknown
        获取指针位置
        pass

    def truncate(self, *args, **kwargs): # real signature unknown
        截断数据,仅保留指定之前数据
        pass

    def writable(self, *args, **kwargs): # real signature unknown
        是否可写
        pass

    def write(self, *args, **kwargs): # real signature unknown
        写内容
        pass
复制代码

 文件的修改

文件的数据是存放于硬盘上的,因而只存在覆盖、不存在修改这么一说,我们平时看到的修改文件,都是模拟出来的效果,具体的说有两种实现方式:

方式一:将硬盘存放的该文件的内容全部加载到内存,在内存中是可以修改的,修改完毕后,再由内存覆盖到硬盘(word,vim,nodpad++等编辑器)

import os  # 调用系统模块

with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f:
    data=read_f.read() #全部读入内存,如果文件很大,会很卡
    data=data.replace('alex','SB') #在内存中完成修改

    write_f.write(data) #一次性写入新文件

os.remove('a.txt')  #删除原文件
os.rename('.a.txt.swap','a.txt')   #将新建的文件重命名为原文件
复制代码
import os  # 调用系统模块

with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f:
    data=read_f.read() #全部读入内存,如果文件很大,会很卡
    data=data.replace('alex','SB') #在内存中完成修改

    write_f.write(data) #一次性写入新文件

os.remove('a.txt')  #删除原文件
os.rename('.a.txt.swap','a.txt')   #将新建的文件重命名为原文件
复制代码

方式二:将硬盘存放的该文件的内容一行一行地读入内存,修改完毕就写入新文件,最后用新文件覆盖源文件

 

import os

with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f:
    for line in read_f:
        line=line.replace('alex','SB')
        write_f.write(line)

os.remove('a.txt')
os.rename('.a.txt.swap','a.txt') 
复制代码
import os

with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f:
    for line in read_f:
        line=line.replace('alex','SB')
        write_f.write(line)

os.remove('a.txt')
os.rename('.a.txt.swap','a.txt') 
复制代码

 

基本运算符

运算符

  计算机可以进行的运算有很多种,可不只加减乘除这么简单,运算按种类可分为算数运算、比较运算、逻辑运算、赋值运算、成员运算、身份运算、位运算,今天我们暂只学习算数运算、比较运算、逻辑运算、赋值运算、成员运算

算数运算

以下假设变量:a=10,b=20

比较运算

以下假设变量:a=10,b=20

字符串进行比较的话,使用的是字符对应的ascii码值。

赋值运算

以下假设变量:a=10,b=20

逻辑运算

 

针对逻辑运算的进一步研究:

1、   在没有()的情况下not 优先级高于 and,and优先级高于or,即优先级关系为( )>not>and>or,同一优先级从左往右计算。

2 、  x or y , x为真,值就是x,x为假,值是y;

         x and y, x为真,值是y,x为假,值是x;

例如:

复制代码
1 > 1 or 3 < 4 or 4 > 5 and 2 > 1 and 9 > 8 or 7 < 6
not 2 > 1 and 3 < 4 or 4 > 5 and 2 > 1 and 9 > 8 or 7 < 6 

逻辑运算符的结果到底是什么类型???
结果取决于两个操作数的类型!!!
针对and操作:第一个操作数如果是可以转成False的话,那么第一个操作数的值,就是整个逻辑表达式的值。
如果第一个操作数可以转成True,第二个操作数的值就是整个表达式的值。

针对or操作:第一个操作数如果是可以转成False的话,第二个操作数的值就是整个表达式的值。
如果第一个操作数可以转成True, 第一个操作数的值,就是整个逻辑表达式的值。
复制代码

逻辑运算符规则和短路操作

成员运算:

除了以上的一些运算符之外,Python还支持成员运算符,测试实例中包含了一系列的成员,包括字符串,列表或元组。

判断子元素是否在原字符串(字典,列表,集合)中:

例如:

#print('喜欢' in 'dkfljadklf喜欢hfjdkas')
#print('a' in 'bcvd')
#print('y' not in 'ofkjdslaf')

Python运算符优先级

以下表格列出了从最高到最低优先级的所有运算符:

运算符描述
** 指数 (最高优先级)
~ + - 按位翻转, 一元加号和减号 (最后两个的方法名为 +@ 和 -@)
* / % // 乘,除,取模和取整除
+ - 加法减法
>> << 右移,左移运算符
& 位 'AND'
^ | 位运算符
<= < > >= 比较运算符
<> == != 等于运算符
= %= /= //= -= += *= **= 赋值运算符
is is not 身份运算符
in not in 成员运算符
not and or 逻辑运算符

身份运算符

身份运算符用于比较两个对象的存储单元

# 判断两个标识符是不是引用同一个对象或不同对象,返回布尔值
a = "烟雨江南"
b =a
print(b is a)  # 类似id(b) = id(a)
print(a is b)  # 类似id(a) = id(b

 

小数据池

复制代码
小数据池:
        is 两者之间的id是否相同
        == 两边的数值是否相等
        id 获取该对象的内存地址
        代码块:一个模块,一个函数,一个类,一个文件等都是一个代码块。
        而作为交互方式输入的每个命令都是一个代码块。
如果在同一代码块下,则采用同一代码块下的换缓存机制。
如果是不同代码块,则采用小数据池的驻留机制。
# pycharm 通过运行文件的方式执行下列代码:  这是在同一个文件下也就是同一代码块下,采用同一代码块下的缓存机制。
i1 = 1000
i2 = 1000
print(i1 is i2)  # 结果为True 因为代码块下的缓存机制适用于所有数字
通过交互方式中执行下面代码:   # 这是不同代码块下,则采用小数据池的驻留机制。
>>> i1 = 1000
>>> i2 = 1000
>>> print(i1 is i2)
False  # 不同代码块下的小数据池驻留机制 数字的范围只是-5~256.
#相同代码块下:
            #所有整数,大部分字符串,bool,存储地址相同
#不同代码块下(小数据池):
            #-5-256,字符串长度小于等于20,bool,存储地址相同
复制代码

 

函数部分

函数的参数及返回值

默认参数

1
2
3
4
5
6
7
8
9
10
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变成默认参数非常简单

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

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

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

关键参数

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

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

非固定参数

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

1
2
3
4
5
6
7
8
9
10
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

1
2
3
4
5
6
7
8
9
10
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'}

 代码实例:

复制代码
 1 def print_info(name,age):
 2     print('Name: %s'%name)
 3     print('Age: %s'%age)
 4 
 5 print_info('alex',35) #必须参数
 6 
 7 def print_info(name,age):
 8     print('Name: %s'%name)
 9     print('Age: %s'%age)
10 
11 print_info(age=35,name='alex') #关键字参数
12 
13 def print_info(name,age,sex='male'):
14     print('Name: %s'%name)
15     print('Age: %s'%age)
16     print('Sex: %s'%sex)
17 
18 print_info('alex',35)
19 print_info('wuchao',20)
20 print_info('jinxin',18)
21 print_info('xiaoyu',18,'female') #默认参数(必须放在必须参数的后面)
22 
23 def add(x,y,z): #low加法器
24     print(x+y+z)
25 
26 add(1,2,3)
27 
28 def add(*args): #不定长参数(高大上加法器)
29     #print(args)
30     sum = 0
31     for i in args:
32         sum += i
33     print(sum)
34 
35 add(1,2,3,4,5,6) #传入的是无命名参数
36 
37 def print_info(*args, **kwargs):  # 不定长参数(键值对)
38 
39     print(args)  # ('alex', 35, 'male') 无命名参数被args接收
40     print(kwargs)  # {'jop': 'IT', 'hobby': 'girl', 'height': 188} 声明参数被kwargs接收
41 
42 
43 print_info('alex', 35, 'male', jop='IT', hobby='girl', height=188)  # 传入的是声明参数
44 
45 
46 def print_info(**kwargs):  # 不定长参数(键值对)
47 
48     print(kwargs)  # {'jop': 'IT', 'hobby': 'girl', 'height': 188} 声明参数被kwargs接收
49     for i in kwargs:
50         print('%s:%s'%(i,kwargs[i]))
51 print_info(jop='IT', hobby='girl', height=188)  # 传入的是声明参数
52 
53 #不定长参数的位置
54 #*args放在左边,**kwsrgs放在后边
55 def f(*args,**kwargs): #无命名参数必须放左边,声明参数放右边
56     pass
57 f(1,2,[3,4,5],name='alex')
58 
59 def print_info(sex='male',*args,**kwargs):  # 不定长参数(键值对) 默认参数必须放在不定长参数的左边
60     print(args)
61 
62     print(kwargs)
63     for i in kwargs:
64         print('%s:%s'%(i,kwargs[i]))
65 print_info('female',1,2,3)
66 print_info(1,2,3,'female',name='alex')
67 
68 #函数参数的优先级
69 def dfun(name,age=22,*args,**kwargs):#必须参数-关键字参数-不定长(无命名参数)-不定长(声明参数) 
复制代码

 函数的返回值

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

注意:

  1. 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,so 也可以理解为 return 语句代表着函数的结束
  2. 如果未在函数中指定return,那这个函数的返回值为None
  3. return多个对象,解释器会把这多个对象组装成一个元组作为一个整体结果输出

 

闭包

   一、什么是闭包

1.首先有一个嵌套函数
2.嵌套函数内部函数调用非全局的变量
3.将内部函数名当做返回值返回,在全局调用
怎么去查看闭包??
函数名.__closure__ 返回None 就不是闭包;
二、闭包的特点
1.保护变量
2.可以让一个变量常驻内存(比如爬虫,不用每次都去重新请求网址)
三、闭包的应用
和全局变量一样,它不确定什么时间会被调用,所以会常驻内存,随时等候被调用;
例如爬虫,不用每次都去重新请求网址,重新获得数据;第一次爬取就会把你想要的数据驻留内存中,方便以后调用;
四、闭包函数示例
def test():
    url = 'www.baidu.com'
    def func():
        print(url)  # 引用外部作用域的变量
    return func     # 返回func的内存地址
res = test()
res()   # www.baidu.com
print(test())  # <function test.<locals>.func at 0x0000021AE401A950>

def func1():
    a = 10
    def func2(num):
        nonlocal a   # 可以通过关键字进行修改
        a += num
        print(a)
    return func2
func1()(100)
查看是否是闭包函数,打印结果不是None就是闭包
print(func1().__closure__)


 

迭代器

同时具有__iter__方法和__next__方法的就是迭代器
for循环的机制 就是迭代器;

#优点:
  # - 提供一种统一的、不依赖于索引的迭代方式
  # - 惰性计算,节省内存
#缺点:
  # - 无法获取长度(只有在next完毕才知道到底有几个值)
  # - 一次性的,只能往后走,不能往前退

    示例:
li = [1,2,3]
a = li.__iter__()
print(a.__next__())
print(a.__next__())  #一个一个取
print(a.__next__())
print(a.__next__())  #将可迭代对象内的元素全部取完后在取会抛出'StopIteration'
li = [1,2,3]
a = li.__iter__()
print(a.__next__())
print(a.__next__())  #一个一个取
print(a.__next__())
print(a.__next__())  #将可迭代对象内的元素全部取完后在取会抛出'StopIteration'
示例:
li = [1,2,3,4,6,7,87,8,9,90,0,0,0,0,8,7,7,67,]
em = li.__iter__()
while 1:
    try:
        print(em.__next__())

    except StopIteration:
        break

当出现 StopIteration 直接退出循环
复制代码
li = [1,2,3,4,6,7,87,8,9,90,0,0,0,0,8,7,7,67,]
em = li.__iter__()
while 1:
    try:
        print(em.__next__())

    except StopIteration:
        break

当出现 StopIteration 直接退出循环
复制代码

 

 

递归

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数,记住哦->在函数内部调用其他函数不是函数的嵌套,而在函数内部定义子函数才是函数的嵌套;

举个例子,我们来计算阶乘n! = 1 x 2 x 3 x ... x n,用函数fact(n)表示,可以看出:

fact(n) = n! = 1 x 2 x 3 x ... x (n-1) x n = (n-1)! x n = fact(n-1) x n

所以,fact(n)可以表示为n x fact(n-1),只有n=1时需要特殊处理。于是,fact(n)用递归的方式写出来就是

def fact(n):
    if n==1:
        return 1
    return n * fact(n - 1)

如果我们计算fact(5),可以根据函数定义看到计算过程如下:

===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120

递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。

使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。可以试试fact(1000),递归默认层次,官方说明 1000,实际测试 998/997,

1. 自己玩自己   (自己调用自己本身)
2. 玩的有限制 (有明确结束条件)
修改递归的最大层数
import sys
sys.setrecursionlimit(10000)

 示列:

复制代码
def fun(n):
    print(n)
    if n / 2 == 0:
        return n
    res = fun(int(n/2))
    return res
fun(10)

复制代码

 

 

总结:

    #     1.生成器的本质就是一个迭代器
# 2.生成器一定是一个迭代器,迭代器不一定是一个生成器
# 3.生成器是可以让程序员自己定义的一个迭代器
# 4.生成器的好处,节省内存空间
# 5.生成器的特性 一次性的,惰性机制,从上向下
# 6.send相当于 next+传值,第一次触生成器的时候,如果使用send(None)值必须是None,一般我建议你们使用__next__
# 7. python2 iter() next()
# python3 iter() next() __next__() __iter__()
# 8.yield from 将可迭代对象元素逐个返回

匿名函数:

  • 1、关键字lambda
  • 2、x 代表函数的形参;
  • 3、x+1 代表函数的返回值相当于return,要返回多个结果需加括号 列:lambda x,y,z:(x+1,y+1,z+1)
  • 4、无名字,需定义;
def fun(x):
    return x+1
print(fun(10))

fun = lambda x:x+1
print(fun(10))

 

 

内置函数

print(abs(-1))#取绝对值
print(all([1,2,3]))#用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False。元素除了是 0、空、FALSE 外都算 TRUE。
print(any([1,2,3]))#用于判断给定的可迭代参数 iterable 是否全部为 False,则返回 False,如果有一个为 True,则返回 True。元素除了是 0、空、FALSE 外都算 TRUE。
print(bin(10))#返回一个整数 int 或者长整数 long int 的二进制表示。
print(hex(10))#用于将10进制整数转换成16进制,以字符串形式表示。
print(oct(10))#函数将一个整数转换成8进制字符串。
print(chr(97))#返回值是当前整数对应的ascii字符。
print(bool(0))#用于将给定参数转换为布尔类型,如果没有参数,返回 False。
print(dict(a="1"))#用于创建一个字典
print(dir(list))#函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息。
print(divmod(10,3))#函数把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b, a % b)。
print(list(enumerate(["lw","xa","ww"])))#用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
print(eval("2+2"))#用来执行一个字符串表达式,并返回表达式的值。
def is_odd(n):
    return n % 2 == 1
newlist = filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])#用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。
print(list(newlist))
print(float(1))#用于将整数和字符串转换成浮点数。
#"{1} {0} {1}".format("hello", "world")  # 设置指定位置,format 函数可以接受不限个参数,位置可以不按顺序。
print(globals())#以字典类型返回当前位置的全部全局变量。
print(hash("test"))#用于获取取一个对象(字符串或者数值等)的哈希值。
print(help(dict))#用于查看函数或模块用途的详细说明。
print(id(list))#用于获取对象的内存地址。
print(int("13",8))#用于将一个字符串或数字转换为整型。8代表进制数;
print(len("asdfghj"))#返回对象(字符、列表、元组等)长度或项目个数。
print(max(10,90,1000))#返回给定参数的最大值,参数可以为序列。
print(min(10,90,1000))#返回给定参数的最小值,参数可以为序列。
p = {"name":"alex","age":18,"hobby":"gilr"}
print(list(zip(p.keys(),p.values())))#拉链函数,两个参数均为序列,用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。

 

高阶函数:就是把函数当成参数传递的一种函数

1、函数名可以进行赋值;

2、函数名可以作为函数参数,还可以作为函数的返回值;

a、函数是第一类对象

b、函数可以被赋值

c、可以被当做参数

d、可以当做返回值

e、可以作为容器类型的元素

复制代码
def f(n):
    return n*n
def foo(a,b,func):
    ret = func(a) + func(b)
    return ret
foo(1,2,f)
print(foo(1,2,f))
#传入参数a=1,b=2,func=f;
#ret = f(1) + f(2);
#f(1)=1,f(2)=4此时调用f函数;
#ret=5;
复制代码

map函数:

描述

map() 会根据提供的函数对指定序列做映射。

第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。

语法

map() 函数语法:

1
map(function, iterable, ...)

参数

  • function -- 函数
  • iterable -- 一个或多个序列

返回值

Python 2.x 返回列表。

Python 3.x 返回迭代器。

实例

1
2
3
4
5
6
7
8
9
10
11
>>>def square(x) :            # 计算平方数
...     return ** 2
...
>>> map(square, [1,2,3,4,5])   # 计算列表各个元素的平方
[1491625]
>>> map(lambda x: x ** 2, [12345])  # 使用 lambda 匿名函数
[1491625]
  
# 提供了两个列表,对相同位置的列表数据进行相加
>>> map(lambda x, y: x + y, [13579], [246810])
[37111519]
复制代码
msg = [1,12,33,42,15,16]
#需求自增1
def add_one(x):
    return x+1
#需求自减1
def reduce(x):
    return x-1
#需求平方
def pf(x):
    return x**2
#实现逻辑
def fangfa(func,red):
    ret = []
    for i in red:
        res = func(i)
        ret.append(res)
    return ret
print(fangfa(add_one,msg))
print(fangfa(reduce,msg))
print(fangfa(pf,msg))

print(fangfa(lambda x:x+1,msg)) #用lambda函数替换add_one函数
print(fangfa(lambda x:x-1,msg)) #用lambda函数替换requce函数
print(fangfa(lambda x:x**2,msg))#用lambda函数替换pf函数

print(list(map(lambda x:x+1,msg))) #map处理的结果是一个可迭代对象,python3中需用list转换;
print(list(map(lambda x:x-1,msg))) #map的第一个参数是逻辑,第二个参数是可迭代对象;
print(list(map(lambda x:x**2,msg)))
复制代码

 filter函数:

描述

filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。

该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。

语法

以下是 filter() 方法的语法:

1
filter(function, iterable)

参数

  • function -- 判断函数。
  • iterable -- 可迭代对象。

返回值

返回列表

实例

以下展示了使用 filter 函数的实例:

1
2
3
4
5
6
7
def is_odd(n):
    return % 2 == 1
  
newlist = filter(is_odd, [12345678910])
print(newlist)
 
[13579]
复制代码
lise_l = ["sb_asd","sb_we","sb_ig","rng"]
#需求去掉开头是sb的
def sh_show(n):
    return n.startswith("sb")
#实现逻辑
def filter_test(func,array):
    red = []
    for i in array:
        if not sh_show(i):
            red.append(i)
    return red
print(filter_test(sh_show,lise_l))

print(filter_test(lambda n:n.startswith("sb"),lise_l))#lambda

print(list(filter(lambda n:not n.startswith("sb"),lise_l)))#filter
复制代码

 reduce函数

描述

reduce() 函数会对参数序列中元素进行累积。

函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。

在 Python3 中,reduce() 函数已经被从全局名字空间里移除了,它现在被放置在 fucntools 模块里,如果想要使用它,则需要通过引入 functools 模块来调用 reduce() 函数:

1
from functools import reduce
1
2
3
4
5
6
7
from functools import reduce
 
def add(x,y):
    return + y
 
print (reduce(add, range(1101)))
  

语法

reduce() 函数语法:

1
2
reduce(function, iterable[, initializer])
  

参数

  • function -- 函数,有两个参数
  • iterable -- 可迭代对象
  • initializer -- 可选,初始参数

返回值

返回函数计算结果。

实例

以下实例展示了 reduce() 的使用方法:

1
2
3
4
5
6
7
>>>def add(x, y) :            # 两数相加
...     return + y
...
>>> reduce(add, [1,2,3,4,5])   # 计算列表和:1+2+3+4+5
15
>>> reduce(lambda x, y: x+y, [1,2,3,4,5])  # 使用 lambda 匿名函数
15

 

装饰器

装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足:

  1. 不能修改被装饰的函数的源代码
  2. 不能修改被装饰的函数的调用方式
  3. 满足1、2的情况下给程序增添功能

那么根据需求,同时满足了这三点原则,这才是我们的目的。因为,下面我们从解决这三点原则入手来理解装饰器。

等等,我要在需求之前先说装饰器的原则组成:

< 函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器 >

这个式子是贯穿装饰器的灵魂所在!

复制代码
装饰器:本质就是函数,功能是为其他函数添加功能;
#原则
#1:不能修改被修饰函数的源代码;
#2:不能修改被修饰函数的调用方式
#装饰器的知识储备
#装饰器 = 高阶函数+函数嵌套+闭包
import time
def demo(func):
    def data():
        star_time = time.time()
        func() 
        end_time = time.time()
        print("执行时间%s"%(end_time - star_time))return data
@demo #相当于inner=demo(inner)
def inner():
    time.sleep(1)
    print("执行结果")
inner()
# inner() #执行的是data
# inner=demo(inner) #返回的是data的地址
# print(inner)# ,<function demo.<locals>.data at 0x00000128A495E950>
复制代码
给函数加上认证功能:


复制代码
user_list = [
    {"name":"alex","passwd":"123"},
    {"name":"lw","passwd":"456"},
    {"name":"szx","passwd":"000"},
]#账户数据库
current_dic = {"username":None,"login":False} #当前登陆状态
def auth_func(func):
    def wrapper(*args,**kwargs):
        if current_dic["username"] and current_dic["login"]:#判断当前登录状态为真时,执行函数;
            res = func(*args, **kwargs)
            return res
        username = input("用户名:")
        passwd = input("密码:")
        for user_dic in user_list: #遍历账户列表
            if username == user_dic["name"] and passwd == user_dic["passwd"]:
                current_dic["username"] = username #更改登录状态
                current_dic["login"] = True
                res = func(*args,**kwargs)
                return res
        else:
            print("用户名或者密码错误")

    return wrapper

@auth_func
def index():
    print("欢迎来到京东主页")
@auth_func
def home(name):
    print("欢迎回家%s" %name)
@auth_func
def shopping_car(name):
    print("%s购物车里有娃娃、奶茶"%(name))
index()
home("老王")
shopping_car("老王")
复制代码
2、需求的实现
假设有代码:

1
2
3
4
5
improt time
def test():
    time.sleep(2)
    print("test is running!")
test()
 很显然,这段代码运行的结果一定是:等待约2秒后,输出

1
test is running
那么要求在满足三原则的基础上,给程序添加统计运行时间(2 second)功能
在行动之前,我们先来看一下文章开头提到的原因1(关于函数“变量”(或“变量”函数)的理解)

2.1、函数“变量”(或“变量”函数)
假设有代码:

1
2
3
4
5
x = 1
y = x
def test1():
    print("Do something")
test2 = lambda x:x*2
 那么在内存中,应该是这样的:



很显然,函数和变量是一样的,都是“一个名字对应内存地址中的一些内容”
那么根据这样的原则,我们就可以理解两个事情:

test1表示的是函数的内存地址
test1()就是调用对在test1这个地址的内容,即函数
如果这两个问题可以理解,那么我们就可以进入到下一个原因(关于高阶函数的理解)

2.2高阶函数
那么对于高阶函数的形式可以有两种:

把一个函数名当作实参传给另外一个函数(“实参高阶函数”)
返回值中包含函数名(“返回值高阶函数”)
那么这里面所说的函数名,实际上就是函数的地址,也可以认为是函数的一个标签而已,并不是调用,是个名词。如果可以把函数名当做实参,那么也就是说可以把函数传递到另一个函数,然后在另一个函数里面做一些操作,根据这些分析来看,这岂不是满足了装饰器三原则中的第一条,即不修改源代码而增加功能。那我们看来一下具体的做法:

还是针对上面那段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
improt time
 
def test():
    time.sleep(2)
    print("test is running!")
 
def deco(func): 
    start = time.time()
    func() #2
    stop = time.time()
    print(stop-start)
 
deco(test) #1
 我们来看一下这段代码,在#1处,我们把test当作实参传递给形参func,即func=test。注意,这里传递的是地址,也就是此时func也指向了之前test所定义的那个函数体,可以说在deco()内部,func就是test。在#2处,把函数名后面加上括号,就是对函数的调用(执行它)。因此,这段代码运行结果是:

1
2
test is running!
the run time is 3.0009405612945557
 

我们看到似乎是达到了需求,即执行了源程序,同时也附加了计时功能,但是这只满足了原则1(不能修改被装饰的函数的源代码),但这修改了调用方式。假设不修改调用方式,那么在这样的程序中,被装饰函数就无法传递到另一个装饰函数中去。

那么再思考,如果不修改调用方式,就是一定要有test()这条语句,那么就用到了第二种高阶函数,即返回值中包含函数名

如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
improt time
 
def test():
    time.sleep(2)
    print("test is running!")
 
def deco(func): 
    print(func)
    return func
t = deco(test) #3
#t()#4
 
test()
 

我们看这段代码,在#3处,将test传入deco(),在deco()里面操作之后,最后返回了func,并赋值给t。因此这里test => func => t,都是一样的函数体。最后在#4处保留了原来的函数调用方式。
看到这里显然会有些困惑,我们的需求不是要计算函数的运行时间么,怎么改成输出函数地址了。是因为,单独采用第二张高阶函数(返回值中包含函数名)的方式,并且保留原函数调用方式,是无法计时的。如果在deco()里计时,显然会执行一次,而外面已经调用了test(),会重复执行。这里只是为了说明第二种高阶函数的思想,下面才真的进入重头戏。

2.3 嵌套函数
嵌套函数指的是在函数内部定义一个函数,而不是调用,如:

1
2
3
4
5
6
def func1():
    def func2():
        pass
而不是
def func1():
    func2()
另外还有一个题外话,函数只能调用和它同级别以及上级的变量或函数。也就是说:里面的能调用和它缩进一样的和他外部的,而内部的是无法调用的。

那么我们再回到我们之前的那个需求,想要统计程序运行时间,并且满足三原则。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
improt time
 
def timer(func) #5
    def deco(): 
        start = time.time()
        func()
        stop = time.time()
        print(stop-start)
    return deco
 
test = timer(test) #6
 
def test():
    time.sleep(2)
    print("test is running!")  
test() #7
 

这段代码可能会有些困惑,怎么忽然多了这么多,暂且先接受它,分析一下再来说为什么是这样。

首先,在#6处,把test作为参数传递给了timer(),此时,在timer()内部,func = test,接下来,定义了一个deco()函数,当并未调用,只是在内存中保存了,并且标签为deco。在timer()函数的最后返回deco()的地址deco。

然后再把deco赋值给了test,那么此时test已经不是原来的test了,也就是test原来的那些函数体的标签换掉了,换成了deco。那么在#7处调用的实际上是deco()。

那么这段代码在本质上是修改了调用函数,但在表面上并未修改调用方式,而且实现了附加功能。

那么通俗一点的理解就是:
把函数看成是盒子,test是小盒子,deco是中盒子,timer是大盒子。程序中,把小盒子test传递到大盒子temer中的中盒子deco,然后再把中盒子deco拿出来,打开看看(调用)

这样做的原因是:

我们要保留test(),还要统计时间,而test()只能调用一次(调用两次运行结果会改变,不满足),再根据函数即“变量”,那么就可以通过函数的方式来回闭包。于是乎,就想到了,把test传递到某个函数,而这个函数内恰巧内嵌了一个内函数,再根据内嵌函数的作用域(可以访问同级及以上,内嵌函数可以访问外部参数),把test包在这个内函数当中,一起返回,最后调用这个返回的函数。而test传递进入之后,再被包裹出来,显然test函数没有弄丢(在包裹里),那么外面剩下的这个test标签正好可以替代这个包裹(内含test())。

 



至此,一切皆合,大功告成,单只差一步。

3、 真正的装饰器
根据以上分析,装饰器在装饰时,需要在每个函数前面加上:

1
test = timer(test)
 显然有些麻烦,Python提供了一种语法糖,即:

1
@timer
这两句是等价的,只要在函数前加上这句,就可以实现装饰作用。

以上为无参形式

4、装饰有参函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
improt time
 
def timer(func)
    def deco(): 
        start = time.time()
        func()
        stop = time.time()
        print(stop-start)
    return deco
 
@timer
def test(parameter): #8
    time.sleep(2)
    print("test is running!")  
test()
 

对于一个实际问题,往往是有参数的,如果要在#8处,给被修饰函数加上参数,显然这段程序会报错的。错误原因是test()在调用的时候缺少了一个位置参数的。而我们知道test = func = deco,因此test()=func()=deco()
,那么当test(parameter)有参数时,就必须给func()和deco()也加上参数,为了使程序更加有扩展性,因此在装饰器中的deco()和func(),加如了可变参数*agrs和 **kwargs。

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
improt time
 
def timer(func)
    def deco(*args, **kwargs): 
        start = time.time()
        func(*args, **kwargs)
        stop = time.time()
        print(stop-start)
    return deco
 
@timer
def test(parameter): #8
    time.sleep(2)
    print("test is running!")  
test()
 那么我们再考虑个问题,如果原函数test()的结果有返回值呢?比如:

1
2
3
4
def test(parameter):
    time.sleep(2)
    print("test is running!")  
    return "Returned value"
那么面对这样的函数,如果用上面的代码来装饰,最后一行的test()实际上调用的是deco()。有人可能会问,func()不就是test()么,怎么没返回值呢?

其实是有返回值的,但是返回值返回到deco()的内部,而不是test()即deco()的返回值,那么就需要再返回func()的值,因此就是:

1
2
3
4
5
6
7
8
9
def timer(func)
    def deco(*args, **kwargs): 
        start = time.time()
        res = func(*args, **kwargs)#9
        stop = time.time()
        print(stop-start)
        return res#10
    return deco
  
其中,#9的值在#10处返回。

完整程序为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
improt time
 
def timer(func)
    def deco(*args, **kwargs): 
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop-start)
        return res
    return deco
 
@timer
def test(parameter): #8
    time.sleep(2)
    print("test is running!")  
    return "Returned value"
test()
 5、带参数的装饰器
又增加了一个需求,一个装饰器,对不同的函数有不同的装饰。那么就需要知道对哪个函数采取哪种装饰。因此,就需要装饰器带一个参数来标记一下。例如:

1
@decorator(parameter = value)
 比如有两个函数:

1
2
3
4
5
6
7
8
9
10
def task1():
    time.sleep(2)
    print("in the task1")
 
def task2():
    time.sleep(2)
    print("in the task2")
 
task1()
task2()
 要对这两个函数分别统计运行时间,但是要求统计之后输出:

1
the task1/task2 run time is : 2.00……
 于是就要构造一个装饰器timer,并且需要告诉装饰器哪个是task1,哪个是task2,也就是要这样:

1
2
3
4
5
6
7
8
9
10
11
12
@timer(parameter='task1') #
def task1():
    time.sleep(2)
    print("in the task1")
 
@timer(parameter='task2') #
def task2():
    time.sleep(2)
    print("in the task2")
 
task1()
task2()
 那么方法有了,但是我们需要考虑如何把这个parameter参数传递到装饰器中,我们以往的装饰器,都是传递函数名字进去,而这次,多了一个参数,要怎么做呢?
于是,就想到再加一层函数来接受参数,根据嵌套函数的概念,要想执行内函数,就要先执行外函数,才能调用到内函数,那么就有:

1
2
3
4
5
6
7
8
9
10
11
def timer(parameter): #
    print("in the auth :", parameter)
 
    def outer_deco(func): #
        print("in the outer_wrapper:", parameter)
 
        def deco(*args, **kwargs):
 
        return deco
 
    return outer_deco
 首先timer(parameter),接收参数parameter=’task1/2’,而@timer(parameter)也恰巧带了括号,那么就会执行这个函数, 那么就是相当于:

1
2
timer = timer(parameter)
task1 = timer(task1)
 后面的运行就和一般的装饰器一样了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import time
 
def timer(parameter):
 
    def outer_wrapper(func):
 
        def wrapper(*args, **kwargs):
            if parameter == 'task1':
                start = time.time()
                func(*args, **kwargs)
                stop = time.time()
                print("the task1 run time is :", stop - start)
            elif parameter == 'task2':
                start = time.time()
                func(*args, **kwargs)
                stop = time.time()
                print("the task2 run time is :", stop - start)
 
        return wrapper
 
    return outer_wrapper
 
@timer(parameter='task1')
def task1():
    time.sleep(2)
    print("in the task1")
 
@timer(parameter='task2')
def task2():
    time.sleep(2)
    print("in the task2")
 
task1()
task2()
 至此,装饰器的全部内容结束。
装饰器示例

 

 python中带*号的参数,一个星号,两个星号

1.带一个星号(*)参数的函数传入的参数存储为一个元组(tuple)

2.带两个星号(*)参数的函数传入的参数则存储为一个字典(dict),并且再调用是采取a=1,b=2,c=3的形式

3.传入的参数个数不定,所以当与普通参数一同使用时,必须把带星号的参数放在最后。

4.函数定义的时候,再函数的参数前面加星号,将传递进来的多个参数转化为一个对象,一个星号转换成元组,两个星号转换成字典,相当于把这些参数收集起来

5.参数前加一个星号,将传递进来的参数放在同一个元组中,该参数的返回值是一个元组

6.参数前两个星号,将传递进来的参数放到同一个字典中,该参数返回值为一个字典

def function_with_one_star(*d):

    print(d[0], type(d))


def function_with_two_stars(**d):
    print(d.get("a",0), type(d))


# 上面定义了两个函数,分别用了带一个星号和两个星号的参数,它们是什么意思,运行下面的代码:
function_with_one_star(1, 2, 3)
function_with_two_stars(a=1, b=2, c=3)

# # 结果如下
# (1, 2, 3) 
#
#
# class 'tuple'>
#
#
# {'a': 1, 'c': 3, 'b': 2} << / span >
#
#
# class 'dict'>
#

 

 

ok

posted @ 2020-03-24 16:41  AnthonyWang  阅读(301)  评论(0)    收藏  举报