s14 第4天 关于python3.0编码 函数式编程 装饰器 列表生成式 生成器 内置方法
python3 编码默认为unicode,unicode和utf-8都是默认支持中文的。
如果要python3的编码改为utf-8,则或者在一开始就声明全局使用utf-8
#_*_coding:utf-8_*_
或者将字符串单独声明:
a = "中文".encode("utf-8")
函数式编程
函数式编程中的函数指代的是数学中的函数。
函数式编程输入如果确定,则输出一定是确定的。函数中并没有很多逻辑运算
python是一个面向对象的语言,只是一部分支持函数式编程。
装饰器:
定义:本质是一个函数,装饰其他函数,为其他函数增加附加功能
原则:
1、不能修改被装饰的函数的源代码
2、不能修改被装饰的函数的调用方式
实现装饰器的知识准备:
1、函数即"变量"
定义函数的过程实际是将函数体以字符串的形式保存进内存的过程,当函数()时才是调用执行函数。
因此函数的定义:
def 函数名():
函数体
实际等价于
函数名= “函数体”
因此函数名就是一个变量名。
可以作为一个参数传递给另一个函数
2、高阶函数
a:把一个函数名当做实际参数传递给另一个函数
可以在不修改函数的源代码的情况下实现新功能
def bar():
print("in the bar")
def new_func(func):
print("new_func")
func()
print("after")
原来调用:bar()
新功能调用为:new_func(bar) ,就增加新功能了,但是改变了原函数的调用方法
b:返回值中包含函数名
不修改原函数的调用方式情况下实现新功能
def bar():
print("in the bar")
def new_func(func):
print("new_func")
func()
print("after")
return func
bar = new_func(bar)
bar() # 为改变原函数的调用方式
返回结果
new_func # new_func功能
in the bar # new_func功能
after # new_func功能
in the bar # bar功能
3、嵌套函数
在一个函数的函数体内用def去定义一个新函数
高阶函数+嵌套函数 => 装饰器
装饰器的写法:
1、普通装饰器
def bar():
print("in the bar")
def test(func):
def new_func():
print("new_func")
func()
print("after")
return new_func
@test
bar()
2、装饰器传递参数:
def bar():
print("in the bar")
def test(func)
def new_func(*args,**kwargs):
print("new_func")
func(*args,**kwargs)
print("after")
return new_func
@test
bar("liubo",30)
3、装饰器带参数,即可以在装饰器内做一些判断:
import time
def decorator(type):
def outer(func):
if type=="1":
definner(*args,**kwargs):
start_time=time.time()
ret=func(*args,**kwargs)
stop_time=time.time()
print("func running time is %s" % (stop_time-start_time))
return ret
return inner
else:
def inner(*args,**kwargs):
print("your input is wrong")
return inner
return outer
print("请输入type:")
type=input(">>>")
@decorator(type)
def f1(name,age):
time.sleep(3)
print("your name is{},age is{}".format(name,age))
f1("liubo",30)
列表生成式
a = [i*2 for i in range(10)]
生成的a为
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
改列表生成器等价于
a = []
for i in range(10):
a.append(i*2)
print(a)
列表生成器可以使代码很简洁,任何针对i的for循环都可以写为[i的处理 for循环]的格式
当连续调用2次列表生成器对同一个列表时,结果并不会叠加2次的数据,而是第二次的数据会覆盖第一次的数据
a = [i*2 for i in range(10)]
a = [i*3 for i in range(10)]
print(a)
结果
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
生成器:
如果执行一个循环的结果非常巨大,而我们只用其中一部分内容,那么多余的数据就是无用的。因此我们需要一种工具在执行循环时,每次循环生成一个数据,而之前的数据会释放,而后面的数据并没有生成,不会占地方。这种工具就是生成器。
上面提到的列表生成式,实际就是在生成时就将列表里的元素全部加载进内存的。
如果:(i*2 for i in range(10)),这样就变成了一个生成器,当输入这个命令时,内存中并没有生成任何元素,只是增加了一个生成器
因此获取生成器中的元素就不能用类似列表的切片的方式获取。
生成器只有在调用时才会生成相应的数据,调用可以使用for循环来生成,也可以使用__next__()方法获取下一个元素。而生成器只有调用时保留当前值,因此生成器无法取上一个值。实际for循环就是在for内部反复的调用next方法
如何生成一个生成器?
一个裴波那契数列的生成器:
def fib(max):
n,a,b = 0,0,1
while n < max:
yield b
a,b = b,a+b
n+=1
return "---done----"
f = fib(10) # 创建生成器
此时f就是一个生成器,那么我要应用这个函数中元素就需要使用next的方式调用。
print(f.__next__())
print(f.__next__())
print(f.__next__())
从上面的例子我们可以看到,生成器就是在函数中出现了yield关键字的函数,当函数运行时,遇到yield函数会暂停,并且返回yield后面的值,这样每次我们调用__next__()方法时,程序都会返回当前运行的结果。而return的内容就是异常时产生的消息
生成器的好处
1、节约内存外
2、可以中断数据的生成去做其他的事,然后再返回到生成器中
print(f.__next__())
print("....") # 中断生成器
print(f.__next__()) # 再次进入生成器
print("xxx") #再次中断生成器
print(f.__next__())
通过__next__()方法,当超过了生成器的元素数量时,就会以异常结束,异常的提示就是程序最后返回的值
StopIteration: ----done---- # 异常
那么如果需要避免这个异常,则需要将异常抓出
g = fib(6)
while True:
try: # 如果正常,则尝试循环
x = next(g)
print("g:",x)
except StopIteration as e: # 如果出现了StopIteration ,则按下面执行
print("Generator retrun value:",e.value) # e.value就是出错的返回值,此处为"---done----"
break
调用一个生成器
def f1():
x = 0
while x < 10:
yield x # 第一次调用next会生成0
x += 1 # 第二次next会先加1 然后输出1
f2 = f1() # 首先定义f2,调用f1函数。此时相当于从f2进入了f1函数
print(f2.__next__()) # 之后的每次调用都是从f2 = f1()这里进入的,因此会依次输出0-4
print(f2.__next__())
print(f2.__next__())
print(f2.__next__())
print(f2.__next__())
如果如下
print(f1().__next__()) # 这样相当于每次都是从f1从新进入,因此每次输出都是0。因为每次都是重新调用函数
print(f1().__next__())
print(f1().__next__())
print(f1().__next__())
print(f1().__next__())
在调用生成器的函数中,除了next方法,还有一个send方法。
next方法仅仅是唤醒生成器的yield,让他开始生成元素,但是如果yield没有返回值,则返回值为None。
send方法唤醒yield,同时给yield传递值。
例如
在生成器函数中
baozi = yield
上面这个函数,取一个变量等于yield,程序 运行到此会暂停,但是yield为空,因此如果我用next方法下面调用baozi这个变量,值为空。
但是如果我在调用send方法,则这个值就会被send发送进来。
def cus(name)
print("%s 开始吃包子" % name)
baozi = yield # 包子赋值给yield,但是并没有返回值,这里的作用是停下来同时接收send的值给baozi
print("%s 的包子已经被我吃了" % baozi) # 此处却需要调用baozi这个变量。
那么如果我需要baozi这个变量有值,就需要这样调用:
c = cus("liubo") # 创建生成器并命名为c
c.__next__() # 首先要让生成器运行到yield。
c.send("韭菜馅") # 调用send方法,将"韭菜馅"这个字符串传递给yield,并向下执行
生成器单线程的并行效果 执行案例
import time
def cus(name):
print("%s 开始吃包子" % name)
while True:
baozi = yield # 包子赋值给yield,但是并没有返回值
print("{}:{} 个包子已经被我吃了".format(name,baozi)) # 此处却需要调用baozi这个变量。
def producer(name):
c1 = cus("c1") # 创建生成器
c2 = cus("c2")
c1.__next__() # 让生成器执行到yield,同时也打印cus函数的第一句话,代表c1已准备好
c2.__next__()
for i in range(1,4):
time.sleep(1)
print("{}:我做了2个包子".format(name))
time.sleep(1)
c1.send(i) # 将i传递给c1,i将会被传递给baozi变量以便打印
time.sleep(1)
c2.send(i)
producer("liubo")
上述程序,将使一个厨师和两个食客三个程序同时运行(但实际是串行的)
迭代器
可直接作用于for循环的数据类型:
1、集合数据类型,例如list,tuple,dict,set,str等
2、生成器,包括带yield的生成器
可以直接作用于for循环的对象统称为可迭代对象,Iterable
可以被__next__()函数调用并不断返回下一个值的对象统称为迭代器 Iterator
可以使用isinstance(),判断一个对象是否为Iterable对象
form collections import Iterator
isinstance((x for x in range(10)),Iterator) # isinstance(对象,类型) 判断对象是否为特定类型
可迭代对象不一定是迭代器(list,tuple,dict),但是可以使用iter()函数将其变为迭代器
a = [11,22,33] # 可迭代对象 不是迭代器
b = iter(a) # 就是一个迭代器了,可以用b.__next__()调用下一个元素
迭代器在python代表一个数据流,是一个惰性运算,即只有当用到这个值时才生成这个值,因此迭代器可以代表一个无穷大的序列。
可迭代对象在创建之初即需要创建所有的元素,迭代器的长度是未知的,但是可迭代对象的长度却是已知的。因此可迭代对象不能是迭代器,只能通过iter()函数转换。
内置方法
内置方法 |
功能 |
案例 |
|
abs(x) |
取一个数的绝对值 |
|
|
all(iterable) |
如果可迭代对象中的所有元素都是True,则返回镇;否则返回False |
print(all([0,1,-3])) |
|
any(iterable) |
如果可迭代对象中有一个元素为真,则返回真。与all相对 |
|
|
ascii(object) |
将一个对象在acsii中的对应打印出来 |
|
不常用 |
bin(x) |
将一个十进制转换为二进制 |
bin(255) 输出0b11111111 |
|
bool(x) |
返回一个值的真假,空和0都为假 |
|
|
bytearray(str,encoding) |
默认情况下,字符串和二进制类型都是不允许修改的,但是bytearray是可以修改的 |
b = bytearray("abcde",encoding ="utf-8") print(b) 输出:a2cde,在这里第二个元素b就被替换为acsii表中第50个元素了。在这种情况下我使用b[1]的方式获取的并非b,而是b在ascii中对应的序号。
但是如果直接用b = byte("abcde",encoding ="utf-8"),则针对某个元素的修改实际都是新建这个列表或字符串,而不是在原来的基础上修改 |
不常用 |
callable() |
判断一个对象是否可以调用,即对象是否可以在最后加() |
def test:pass print(callable(test))
输出 True |
|
chr(i) |
输入一个数字,返回acsii表中对应的元素 |
char(97) 输出"a" |
|
ord() |
输入一个元素,返回ascii表中对应的序号 |
ord("a") 输出97 |
|
classmethod(func) |
|
|
|
compile (code,"文件名",执行方式) |
将一个字符串编译为可以被python执行的语句(此时仍未执行)。 其中code是被编译的字符串,文件名是当出错时日志输出的位置,执行方式是希望被那个命令执行 |
code = "for i in range(10)" c = complie(code,"","exec") exec(c) 输出,执行code的代码。此处使用的exec执行命令 |
|
dict() |
生成一个字典 |
|
|
dir() |
可以查询对象的内部方法 |
dir([]) 输出列表的方法 ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__' , '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__' , '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__' , '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_e x__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__s izeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'ex tend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] 其中__的是内部方法不可用 |
|
divmod(a,b) |
a和b相除与商和余数 |
x,y = divmod(5,2) print("x=",x) print("y=",y)
输出: x=2 y=1
2为商,1为余数 |
|
enumerate(iterable,start=0) |
将一个可迭代对象序列化,开始的序列为start=中定义的。将可迭代对象中每个元素与其序号单独形成一个新的元组。每个元组就可以分片获取。start如果不写,默认从0开始 |
li = [11,22,33] for i in enumerate(li,start=1): print(i)
输出: (1, 11) (2, 22) (3, 33)
|
|
eval(表达式,globals=None,locals=None) |
eval()用来将字符串编译为表达式,例如 s = "8*8" r = eval(s) , print(r)的时候会返回为64,eval只能执行表达式,eval有返回值,如果用eval(“7+8+9”),是能返回结果的,这点比exec强,对比exec。 |
x=1 eveal("x+1")
输出
2 |
|
exec(object) |
exec() 用来执行编译后的代码,和complie何用,先用complie编译,然后用exec执行。exec可以执行所有的python的命令,exec只是执行没有返回值,因此用exec运行一个表达式,是拿不到结果的,例如exec(“7+8+9”),只是执行但是结果是拿不到的。在执行代码的时候exec可以接受代码或者字符串。 |
|
|
filter(func,iterable) |
将可迭代对象的每个函数带入func中,生成一个迭代器,迭代的元素时func中返回为真的值 |
def func1(a): if a>22: retrun True b = filter(func1,[11,22,33,44]) for i in b: print(i) 返回 33 44
此处也可以直接跟lambda函数
for i in filter(lambda x : x>22,[11,22,33,44]): print(i)
返回 33 44
|
|
map(func,iterable) |
和filter对比,filter方法将返回为真的值生成新的迭代器。 而map方法则将返回值迭代。 |
for i in map(lambda x:x>22,[11,22,33,44]) print(i)
返回
False False True True
当然就是说map会将func的结果直接返回,如果 for i in map(lambda n : n*2,[11,22,33]): print(i)
返回 22 44 66 |
|
import functools |
是一个二元操作函数,他用来将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给reduce中的函数 func()(必须是一个二元操作函数)先对集合中的第1,2个数据进行操作,得到的结果再与第三个数据用func()函数运算,最后得到一个结果。
|
import functools res = functools.reduce(lambda x,y:x*y,range(1,10)) print(res)
上面的函数的结果就是1*2*3*4.....*9。 如描述里面的func必须是一个二元操作的,那么这里的两个变量就会首先从后面的可迭代对象中取前两个,然后按照要求返回,之后会将返回值与下一个对象操作,知道可迭代对象中所有的元素都被处理。 |
|
frozenset() |
将一个集合变成不可修改的集合。 |
a = set([]) 此时的a可以有pop,clear等方法可以修改这个集合
但是如果用 a = frozenset() a就不在有这些方法了
|
|
globals() |
以字典的形式当前整个代码中的所有变量 |
|
|
hash() |
将一个特定对象进行哈希算法 |
|
|
hex() |
将数字转为十六进制 |
|
|
locals() |
以字典形式打印当前代码中的全部的局部变量 |
|
|
max() |
返回可迭代对象中的最大值 |
|
|
min() |
返回最小值 |
|
|
next() |
从迭代器中返回下一个item |
|
|
oct() |
将一个数字转为八进制 |
|
|
pow(x,y) |
打印x的y次方的结果 |
|
|
round(x,y) |
x是一个浮点数,y则为保留小数点后多少位,默认y为0即不显示小数,结果会四舍五入 |
round(1.2323,2) 输出 1.23 |
|
sorted(排序内容,key="排序依据8") |
排序,只能排序int类型的数字。如果排序字典则字典的key必须是int类型 |
可以将字典排序 a = {1:11,2:22,5:55,3:33,4:44} print(sorted(a)) 输出会将key排序输出 [1, 2, 3, 4, 5]
print(sorted(a.items()))
字典.items()这个方法本身会将字典转为元组 输出结果为 按照字典key排序,并将字典的键值对变为元组 [(1, 11), (2, 22), (3, 33), (4, 44), (5, 55)]
如果要按照字典的值排序 print(sort(a.items(),key=lambda x:x[1]))
其中,a.items()会将字典的键值对变为元组,每个元组的[0]是键,[1]是值 lambda x:x[1] 是将这个元组带入函数,并返回[1]角标即字典的值, key = lambda x : x[1]代表以字典的值为排序依据
返回结果 [(1, 11), (2, 22), (3, 33), (4, 44), (5, 55)]
以值排序,key可以不是int类型
a = {"zhangsan":11,"lisi":22,5:55,3:33,4:44} print(sorted(a.items(),key=lambdax:x[1]))
返回结果
[('zhangsan', 11), ('lisi', 22), (3, 33), (4, 44), (5, 55)] |
|
sum() |
输入列表,将列表求和 |
|
|
zip(列表a,列表b) |
将两个列表的元素一一对应,取列表中元素最少的拼接 |
|
|
__import__() |
正常的import导入模块时后面模块名是一个变量。但是如果模块名是一个字符串,就不能用import。因此需要__import__() |
__import__("decorator") |
|
匿名函数
函数的目的是为了重复调用,为此建立函数在内存中占用空间,但是如果这个函数仅调用一次这个占用的空间就被浪费了。因此引出了匿名函数。
lambda函数即匿名函数,因为这个函数并未像其他函数一样通过def func()的方式创建了一个func的函数,他并没有创建函数名,因此当还行一次后就没有对应的变量与之关联,其占用的内存空间也就会被回收。
def func(n):
print(n)
这个函数的lambda写法为
lambda n:print(n)
执行时可以
(lambda n:print(n))(5)
a = lambda n : print(n)
a(5)
A = lambda n:print(n) # 这里的A是一个函数
A = (lambda n : print(n))(5) # 这里的A是匿名函数的结果,传递了n = 5给匿名函数后的return
但是lambda只能执行简单的操作,比如三元运算
a = lambda n : print("n<3") if n<3 else print("n>3") # 相当于return 返回了一个三目运算
a(5)
软件目录开发规范
os.path.dirname() |
获取当前目录的上级目录名 |
os.path.abspath(__file__) |
获取当前文件的绝对路径 |
sys.path.append() |
追加环境变量的路径 |
因此如果一个文件的绝对路径为:E:\Atm\bin\atm.py,我需要将E:\Atm路径追加到python模块调用的环境变量中,那么需要这样写
base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base)