s14 第4天 关于python3.0编码 函数式编程 装饰器 列表生成式 生成器 内置方法

时间:2023-03-08 16:32:09

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]))
    返回为False,因为0非真,如果print(all[1,2,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")
    b[1] = 50

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
    functools.reduce(func,iterable)

是一个二元操作函数,他用来将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给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)