python 函数、装饰器、迭代器、生成器、列表生成式

时间:2021-09-02 19:52:24

[toc]

函数

函数的参数

位置参数

def foo(x,y,z):#位置形参:必须被传值的参数
    print(x,y,z)

# foo(1,2,3)
foo(1,2,3) #位置实参数:与形参一一对应

默认参数(默认参数必须指向不变对象)

def register(name,age,sex='male'): #形参:默认参数
    print(name,age,sex)

register('asb',age=40)
register('a1sb',39)
register('a2sb',30)
register('a3sb',29)

register('钢蛋',20,'female')
register('钢蛋',sex='female',age=19)

默认参数需要注意的问题

(1):默认参数必须跟在非默认参数后

def register(sex='male',name,age): #在定义阶段就会报错
    print(name,age,sex)

(2):默认参数在定义阶段就已经赋值了,而且只在定义阶段赋值一次

a=100000000
def foo(x,y=a):
    print(x,y)
a=0
foo(1)

(3):默认参数的值通常定义成不可变类型

可变长参数

def foo(x,y,*args): #*会把溢出的按位置定义的实参都接收,以元组的形式赋值给args
    print(x,y)
    print(args)

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


def add(*args):
    res=0
    for i in args:
        res+=i
    return res
print(add(1,2,3,4))
print(add(1,2))



def foo(x, y, **kwargs):  # **会把溢出的按关键字定义的实参都接收,以字典的形式赋值给kwargs
    print(x, y)
    print(kwargs)
foo(1,2,a=1,name='egon',age=18)


def foo(name,age,**kwargs):
    print(name,age)
    if 'sex' in kwargs:
        print(kwargs['sex'])
    if 'height' in kwargs:
        print(kwargs['height'])

foo('egon',18,sex='male',height='185')
foo('egon',18,sex='male')


def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    print(sum)
num = [1,2,3]       #num位置可以是list或tuple
calc(*num)
*nums表示把nums这个list的所有元素作为可变参数传进去。这种写法相当有用,而且很常见。

关键字参数

def foo(x,y,z):
    print(x,y,z)

foo(z=3,x=1,y=2)

关键字参数需要注意的问题:

1:关键字实参必须在位置实参后面
2: 不能重复对一个形参数传值
foo(1,z=3,y=2) #正确
foo(x=1,2,z=3) #错误
foo(1,x=1,y=2,z=3) #错误

命名关键字参数(*号可以把它后面的关键词过滤掉)

def foo(name,age,*,sex='male',height):
    print(name,age)
    print(sex)
    print(height)
#*后定义的参数为命名关键字参数,这类参数,必须被传值,而且必须以关键字实参的形式去传值

foo('egon',17,height='185')
def per(name,age,*,city):
    print(name,age,city)
per('make',24,city='beijing')
def per(name,age,*,city='beijing',job):
    print(name,age,city,job)
per('make',24,job='it')

总结:

*args是可变参数,args接收的是一个tuple;
**kw是关键字参数,kw接收的是一个dict

---

函数对象

返回值可以是函数

def foo():
    print('from foo')

def bar(func):
    #print(func)
    return func
f = bar(foo)  #取到返回值foo内存地址
print(f)
f()         #调用

可以当作参数传递

def foo():
  print('from foo')
def bar(func):
  print(func)
  func()

bar(foo) # foo函数内存地址被当作参数传递到bar函数中,并调用
  
 '''
 结果:
 <function foo at 0x00000049CC9A3E18>
 from foo
 '''

函数嵌套

函数嵌套是闭包函数基础

函数嵌套定义

函数的嵌套定义: 顾名思义就是函数里面, 套函数。应用如闭包、装饰器
def f1():
    def f2():
        print('from f2')
        def f3():
            print('from f3')
        f3()
    f2()
f1()

函数的嵌套调用

函数的嵌套调用:属于面向过程(分子原子级操作),细分问题
def max2(x,y):
    """判断两个数的大小"""
    return x if x>y else y


def max4(a,b,c,d):
    """判断4个数的大小"""
    res1 = max2(a,b)
    res2 = max2(res1,c)
    res3 = max2(res2,d)
    return res3

print(max4(12,34,11,35))

名称空间和作用域

名称空间定义

#倒入模块
import time
#变量赋值
name = 'engon'
#函数定义
def func():
    pass
#类定义
class Foo:
    pass

名称空间分类

内置名称空间:随着python解释器的启动而产生

print(max)
print(min)

#所有的内置空间
#dir 对象的内建名称空间
import builtins
for i in dir(builtins):
    print(i)

全局名称空间

文件的执行会产生全局名称空间,指的是文件级别定义的名字都会放入改空间

局部名称空间

调用函数时会产生局部名称空间,只在函数调用时临时绑定,调用结束解绑定

作用域

为名称空间的具体应用。他们之间的关系,如下对应:
1.全局作用域:内置名称空间,全局名层空间
2.局部作用:局部名称空间

作用于的优先级顺序:局部名称空间---》全局名层空间---》内置名称空间

查看全局作用域内的名字:gloabls()

查看局部作用域内的名字:locals()


闭包函数

如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。一个闭包就是你调用了一个函数A,这个函数A返回了一个函数B给你。这个返回的函数B就叫做闭包。你在调用函数A的时候传递的参数就是*变量

函数嵌套的一种方式,必须遵守以下规则:
a. 定义在内部函数 b. 包含对外部作用域而非全局作用域的引用,该内部函数就成为闭包函数
# 定义实例,对象隐藏,全局不可见
def f1():
# x内部隐藏,全局不可见
    x = 1
    def f2():
        print(x)
    return f2
f=f1()
#print(f)
# x因为隐藏,所以全局作用域无效
x=100000000000000000000000000
f()

#下面也是个闭包
#!/usr/bin/python
# -*- coding:utf-8 -*-
def f1():
    x = 1
    y = 2
    def f2():
        print(x,y)
    return f2
a = f1()

print(a)
print(a.__closure__[0].cell_contents)

闭包应用:惰性计算

from urllib.request import urlopen
#res = urlopen('http://crm.oldboyedu.com').read()
#print(res.decode('utf-8'))

def index(url):
    def get():
        return urlopen(url).read()
    return get
# 存在内存里
oldboy = index('http://crm.oldboyedu.com')
#print(oldboy().decode('utf-8'))
# 闭包函数相对与普通函数会多出一个__closure__的属性,里面定义了一个元组用于存放所有的cell对象,
# 每个cell对象一一保存了这个闭包中所有的外部变量
print(oldboy.__closure__[0].cell_contents)

-----------------------------------------------

内置函数

数学运算 abs(),round(),pow(),divmod(),max(),min(),sum()


abs(-5)  # 取绝对值,也就是5
round(2.623423,5)  # 不带第二个参数(四舍五入取整,也就是3.0, 4为精准到四位四舍五入),否则第二个参数为小数点后面的位数
pow(2, 4)  # 相当于2**3,如果是pow(2, 3, 5),相当于2**3 % 5
divmod(9, 2)  # 返回除法结果和余数
max([1, 5, 2, 9])  # 求最大值
min([9, 2, -4, 2])  # 求最小值
sum([2, -1, 9, 12])  # 求和

工厂函数 int(), float(), str(), bool(),slice(),list(),tuple(), dict(), set(), frozenset()

int("5")  # 转换为整数 integer
float(2)  # 转换为浮点数 float
str(2.3)  # 转换为字符串 string
bool(0)   # 转换为相应的真假值,在Python中,0相当于False在Python中,下列对象都相当于False:[], (), {}, 0, None, 0.0, ''
slice(5, 2, -1)   # 构建下标对象 slice,切片函数
list((1, 2, 3))   # 转换为表 list
tuple([2, 3, 4])  # 转换为定值表 tuple
dict(a=1, b="hello", c=[1, 2, 3])   # 构建词典 dictionary
set()           #创建集合函数
frozenset()     #创建一个不可修改的集合 如:s=frozenset({1,2}) # 定义不可变集合

类型转换 ord(), chr(), bin(), hex(), oct(), complex()

ord('A')        # "A"字符对应的数值
chr(65)         # 数值65对应的字符
bin(56)         # 返回一个字符串,表示56的二进制数
hex(56)         # 返回一个字符串,表示56的十六进制数
oct(56)         # 返回一个字符串,表示56的八进制数
complex(3,9)    # 返回复数 3 + 9j

序列操作 all(), any(), sorted(), reversed()

all([True,1,"hello!"])      # 是否所有的元素都相当于True值
any(["",0,False,[],None])   # 是否有任意一个元素相当于True值
sorted([1,5,3])             # 返回正序的序列,也就是[1,3,5]
reversed([1,5,3])           # 返回反序的序列,也就是[3,5,1]

编译执行函数 repr(), compile(), eval(), exec()

'''
参数mode是用来指明那种表示的源码类型;
如果是exec类型,表示这是一个序列语句,可以进行运行;
如果是eval类型,表示这是一个单一的表达式语句,可以用来计算相应的值出来;
如果是single类型,表示这是一个单一语句,采用交互模式执行,在这种情况下,如果是一个表达式,一般会输出结果,而不是打印为None输出。
'''

a = repr('me')           # 返回一个对象的字符串表示。有时可以使用这个函数来访问操作。
print(a)

b = compile("print('Hello')",'','exec')     # 编译字符串成为code对象
exec(b)

d = "1+2+3+4*4"
e = compile(d,'','eval')             # 解释字符串表达式。参数也可以是compile()返回的code对象
print(eval(e))

b = compile("print('Hello')",'','eval')
eval(b)

帮助函数 dir(), help(), id(), len(), challables()

'''
dir()   不带参数时返回当前范围内的变量,方法和定义的类型列表,带参数时返回参数的属性,方法列表
help()  返回对象的帮助文档
id()    返回对象的内存地址
len()   返回对象长度,参数可以是序列类型(字符串,元组或列表)或映射类型(如字典)
challable()  判断对象是否可以被调用,能被调用的对象就是一个callables对象,比如函数和带有__call__()的实例
'''
l = []
print(dir(l))
print(help(l))
x = 1
y = 2
print(id(x),id(y))
print(y is x)       #判断的是身份

def func():
    pass
print(callable(func))

作用域查看函数 globals(), locals(), vars()

globals()  返回一个描述当前全局变量的字典
locals()   打印当前可用的局部变量的字典
vars()    1. 当函数不接收参数时,其功能和locals函数一样,返回当前作用域内的局部变量。
          2. 当函数接收一个参数时,参数可以是模块、类、类实例,或者定义了__dict__属性的对象。

-----------------------------------------------

装饰器

装饰器:修饰别人的工具,修饰添加功能,工具指的是函数 装饰器本身可以是任何可调用对象,被装饰的对象也可以是任意可调用对象

为什么要用装饰器?

a. 开放封闭原则:对修改是封闭的,对扩展是开放的 
b. 装饰器就是为了在不修改被装饰对象的源代码以及调用方式的前提下,为其添加新功能

1.装饰器的基本写法(可传参数)

import time

def timmer(func):
    def wrapper(*args):
        # 在运行函数前执行,time.time()为当前时间(格林威治时间1970到现在的秒数)
        start_time = time.time()
        # 被装饰函数执行,没有返回值,默认为
        res = func(*args)
        # 在函数运行后执行
        stop_time = time.time()
        print('run time is %s' % (stop_time - start_time))
    return wrapper

@timmer
def ff(args):
    time.sleep(1)
    print('welecome to %s'% args)

ff('china')

2.多实例添加

import time

def timmer(func):
    def wrapper(*args,**kwargs):
        # 在运行函数前执行,time.time()为当前时间(格林威治时间1970到现在的秒数)
        start_time = time.time()
        # 被装饰函数执行,没有返回值,默认为
        res = func(*args,**kwargs)
        # 在函数运行后执行
        stop_time = time.time()
        print('run time is %s' % (stop_time - start_time))
    return wrapper

@timmer
def foo(ar,kw):
    time.sleep(1)
    print('name:%s \nage:%s'% (ar,kw))

@timmer
def ff(job):
    print('job %s' % job)


res = foo('mk','18')
res1 = ff('it')

3.多装饰器,执行顺序(不太明白)

import time

def timmer(func):
    def wrapper(*args):
        # 在运行函数前执行,time.time()为当前时间(格林威治时间1970到现在的秒数)
        start_time = time.time()
        # 被装饰函数执行,没有返回值,默认为
        res = func(*args)
        # 在函数运行后执行
        stop_time = time.time()
        print('run time is %s' % (stop_time - start_time))
    return wrapper

login_user = {'user':None,'status':False}
def auth(func):
    def wrapper(*args,**kwargs):
        if login_user['user'] and login_user['status']:
            res = func(*args,**kwargs)
            return res
        else:
            name = input('name:')
            passwd = input('passwd')
            if name == 'mk' and passwd == '123':
                login_user['user']='mk'
                login_user['status']='123'
                print('\033[45mlogin successful\033[0m')
                res = func(*args,**kwargs)
                return res
            else:
                print('\033[43mlogin err\033[0m')
    return wrapper


@auth
@timmer
def index():
    time.sleep(1)
    print('namenage')

index()

4.有参装饰器

login_user = {'user':None,'status':False}
def auth(db_type):
    def auth2(func):
        def wrapper(*args,**kwargs):
            if db_type == 'file':
                if login_user['user'] and login_user['status']:
                    res = func(*args,**kwargs)
                    return res
                else:
                    name = input('name:')
                    passwd = input('passwd')
                    if name == 'mk' and passwd == '123':
                        login_user['user']='mk'
                        login_user['status']='123'
                        print('\033[45mlogin successful\033[0m')
                        res = func(*args,**kwargs)
                        return res
                    else:
                        print('\033[43mlogin err\033[0m')
            elif db_type == 'ldap':
                print('db_type-->ldap')
        return wrapper
    return auth2

@auth('file')  #传参

def index():
    print('namenage')
index()

# @auth('ldap')
#
# def index():
#     print('namenage')
# index()

5.装饰器@functools.wraps + 有无参装饰器

a.官网装饰器定义:

装饰器是一个函数,其主要用途是包装另一个函数或类。这种包装的首要目的是透明地修改或增强被包 装对象的行为。

b.@functools.wraps作用:

因为装饰器是一个闭包函数,怎样能获取被修饰函数的详细信息,如:__name__。      此类实际调用函数的属性。@functools.wraps就提供了该功能。      它是如何实现的,下面的例子是通过反射的原理把func的一些属性更新给了callf函数。原func的属性      不变。但是callf的属性还是跟原func不完全一样,so,如有类似的问题,还需要自己写。      具体,请看未来的类章节。

functool.wrpaps 保持了wrapper.name = func.name

#!/usr/bin/python
# -*- coding:utf-8 -*-
import functools


def log(text):          #定义装饰器
    if isinstance(text,str):
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args,**kwargs):
                print('%s %s' % (text,func.__name__))
                return func(*args,**kwargs)
            return wrapper
        return decorator
    else:
        @functools.wraps(text)
        def wrapper(a,b):
            print('call %s' % text.__name__)
            return text(a,b)

        return wrapper

#情景一:装饰器本身不用传入参数,只接收函数func
@log          #调用装饰器(将函数now当参数传入log装饰器)
def now(a,b): #可以传值
    print('20159',a,b)
#now()
a = now('c','d')

#情景二:装饰器本身需要传入参数,再接收函数func
@log('ii.py')
def now():
    print('2015')
#now()
#print(now.__name__)

----

迭代器

迭代器只不过是一个实现迭代器协议的容器对象。
可迭代的对象:内置__iter__方法的,都是可迭代的对象

特点:

1、访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
2、不能随机访问集合中的某个值 ,只能从头到尾依次访问
3、访问到一半时不能往回退
4、便于循环比较大的数据集合,节省内存

可迭代对象通过__iter__转成迭代器对象

a = iter([1,2,3,4,5])

print(a)
print(a.__next__())
print(a.__next__())
print(a.__next__())
print(a.__next__())
print(a.__next__())

结果:

<list_iterator object at 0x1028d95c0>
1
2
3
4
5

判断一个对象是可迭代对象

 通过collections模块的Iterable类型判断:
from collections import Iterable
print isinstance('abc', Iterable)   # str是否可迭代
True
print isinstance('1,2,3', Iterable) # list是否可迭代
True
print isinstance(123, Iterable)     # 整数是否可迭代
False

优点

1.提供了一种不依赖下标的迭代方式

l = [122,343,55,6,7]
i = iter(l)
print(i)
print(next(i))
print(next(i))
'''结果
<list_iterator object at 0x1007d9518>
122
343
'''

2.就迭代器本身来说,更节省内存。迭代一个值,原来迭代的值丢弃

缺点

  1. 无法获取迭代器对象的长度
  2. 不如序列类型取值灵活,是一次性的,只能往后取值,不能往前退
    ```
    l = [122,343,55,6,7]
    i = iter(l)
    for item in i:
    print(item)

print('='*10)

    '''结果
    122
    343
    55
    6
    7
    ==========
    '''

for item in i:
print(item)

f = open('a.txt','r',encoding='utf-8')

print(next(f),end='')

for ii in f:
#print(ii)
print(ii.strip())
```
'''结果
222333
sdf
'''

-------------------------------

生成器

一个函数调用时返回一个迭代器,那这个函数就叫做生成器(generator);如果函数中包含yield语法,那这个函数就会变成生成器。

yield使用

def xran():
    print('one')
    yield 1
    print('two')
    yield 2
    print('three')
    yield 3
a = xran()
print(a.__next__())
print(a.__next__())
print(a.__next__())
#a.__next__() #循环完毕抛出StopIteration
a.close     #关闭生成器

生成器表达式

a = [7,8,9]
b = (i**2 for i in a)

print(b)
print(next(b))
print(next(b))
print(next(b))
a. 相当于为函数封装好__iter__和__next__    
b. return只能返回一次值,函数就终止了,而yield能返回多次值,每次返回都会将函数暂停,下一次next会从上一次暂停的位置继续执行

yield程序实例(tail -f a.txt | grep 'python' 类功能python程序版)

import time


def tail(ttt):
    with open(ttt,encoding='utf-8') as f:
        f.seek(0,2)
        #print(f.tell())
        while True:
            line = f.readline().strip()
            if line:
                return line
                yield line
            else:
                time.sleep(0.2)

# 测试tail功能是否有效(用echo ‘python’>> a.txt,不能在pc中)
# t = tail('a.txt')
#print(next(t))
# for line in t:
#     print(line)


def grep(patten,lines):
    for line in lines:
        if patten in line:
            yield line
g = grep('python',tail('a.txt'))
print(g)
for i in g:
    print(i)

----------------------------------

列表生成式

#筛选出仅偶数的平方
a = [x*x for x in range(1,11) if x % 2 ==0]
print(a)          #[4, 16, 36, 64, 100]


b = [m+n for m in 'ABC' for n in 'abc']
print(b)          #['Aa', 'Ab', 'Ac', 'Ba', 'Bb', 'Bc', 'Ca', 'Cb', 'Cc']


import os
c = [d for d in os.listdir('.')] #遍历当前路径下的文件
print(c)            #print([d for d in os.listdir('.')])


d = {'x': 'A', 'y': 'B', 'z': 'C' }
for k,v in d.items():
    print(k,'=',v)
print([k + '=' + v for k,v in d.items()])


L = ['Hello', 'World', 'IBM', 'Apple']
e = [s.lower() for s in L]
print(e)


#内建的isinstance函数可以判断一个变量是不是字符串:
x='abc'
y=123
print(isinstance(x, str))
print(isinstance(y, str))

练习:

请修改列表生成式,通过添加if语句保证列表生成式能正确地执行
# L1 = ['Hello', 'World', 18, 'Apple', None]
# 期待输出: ['hello', 'world', 'apple'] print(L2)

方法一:
L1 = ['Hello', 'World', 18, 'Apple', None]
L2 = []
for i in L1:
    if isinstance(i,str):
        L2.append(i.lower())
print(L2)
方法二:
L2 = [s.lower() for s in L1 if isinstance(s,str)]
print(L2)