Python --函数和函数式编程

目录

1, 什么是函数

2, 调用函数

3, 函数参数

3-1, 位置参数

3-2, 关键字参数

3-3, 默认参数

3-4, 参数组

3-5, 函数作为参数传递

4, 内嵌函数

4-1, 闭包

5, 装饰器

5-1, 无参装饰器

5-2, 有参装饰器

5-3, 多个装饰器

6, lambda函数

7, map函数

8, reduce函数

9, filter函数

10, 偏函数

11, 变量的作用域

11-1, 全局变量

11-2, 局部变量

11-3, 搜索标识符的顺序

11-4, global语句

12, 递归

13, 生成器

14, 迭代器

14-1, 创建迭代器

14-1-1,iter(seq)

14-1-2, 定制迭代器(__iter__)


1, 什么是函数

函数是对程序的逻辑进行结构化的一种编程方法, 不指定返回值,则返回None, 先定义,再使用

2, 调用函数

通过函数名(参数), 进行函数调用

In [31]: def func():
    ...:     print('hello python')

In [32]: func()
hello python

3, 函数参数

3-1, 位置参数

位置参数必须按形参的顺序传入,在没有默认参数的情况,实参和形参的数量一致

位置参数必须要默认参数前

3-2, 关键字参数

关键字参数的概念仅仅针对函数的调用, 允许参数不按顺序传入

In [33]: def func(host, port):
    ...:     print('host:', host, 'port:', port)

# 这里使用关键字参数调用函数,没有按照形参的顺序赋值
In [34]: func(port=8080, host='aaa')
host: aaa port: 8080

3-3, 默认参数

默认参数就是定义默认值的参数,在函数调用时,不传入参数,则参数去默认值传入参数,则按传入参数为准

# 定义默认参数
In [40]: def func(host='aaa', port=8080):
    ...:     print('host:', host, 'port:', port)

# 不传入参数,以默认参数为准
In [41]: func()
host: aaa port: 8080

# 传入参数,则以传入参数为准
In [42]: func(host='aa1', port=8088)
host: aa1 port: 8088

3-4, 参数组

通过func(*args, **kw), 一个元组(非关键字参数),  一个字典(关键字参数)作为参数组传递给函数, args元组形式的非关键字参数, kw字典形式的关键字参数

def func(*args, **kw):
    print('args:', args)
    print('kw:', kw)

if __name__ == '__main__':
    args = (1, 2, 3)
    kw = dict(a='a1', b='b1')
    func(*args, **kw)
    # 这里没有以关键字和非关键字形式传入,args, kw会被当着位置参数传递给*args
    func(args, kw)

# 输出结果
args: (1, 2, 3)
kw: {'a': 'a1', 'b': 'b1'}

args: ((1, 2, 3), {'a': 'a1', 'b': 'b1'})
kw: {}

元组形式非关键参数, 在函数定义时,需要放到默认参数之后

# *args需要放到默认参数之后, 在调用tupleVarArgs,额外参数将以元组形式保存到args中
def tupleVarArgs(arg1, arg2='defaultB', *args):
    print('formal arg 1:', arg1)
    print('formal arg 2:', arg2)
    print('other args:', args)

if __name__ == '__main__':
    # 只传入位置参数情况, args将是空元组
    tupleVarArgs('abc')

# 输出结果:
formal arg 1: abc
formal arg 2: defaultB
other args: ()

if __name__ == '__main__':
    # 多余的'defaultD', 'defaultE'参数,以元组形式存放在args中
    tupleVarArgs('abc', 'defaultC', 'defaultD', 'defaultE')

# 输出结果
formal arg 1: abc
formal arg 2: defaultC
other args: ('defaultD', 'defaultE')

字典形式关键字参数, 在函数定义时,需要放到非关键字参数之后

def dictVarArgs(arg1, arg2='defaultB', *args, **kwargs):
    print('formal arg 1:', arg1)
    print('formal arg 2:', arg2)
    print('args:', args)
    print('kwargs:', kwargs)

if __name__ == '__main__':
    dictVarArgs('abc')

# 输出结果
# 说明:因为函数调用时,值传入了位置参数,所以args, kwargs为空
formal arg 1: abc
formal arg 2: defaultB
args: ()
kwargs: {}

if __name__ == '__main__':
    dictVarArgs('abc', '740', c='haha')

# 说明:额外参数c='haha'以字典形式传入关键字参数
# 输出结果
formal arg 1: abc
formal arg 2: 740
args: ()
kwargs: {'c': 'haha'}


if __name__ == '__main__':
    dictVarArgs('abc', '740', (1, 2, 3), 'a', 'b', c='haha', d='dd')

# 说明:额外参数(1, 2, 3), 'a', 'b', 以元组形式传入非关键字参数
# 额外参数c='haha', d='dd', 以字典形式传入关键字参数中
# 输出结果
formal arg 1: abc
formal arg 2: 740
args: ((1, 2, 3), 'a', 'b')
kwargs: {'c': 'haha', 'd': 'dd'}

3-5, 函数作为参数传递

函数名本质上是函数对象的一个引用,相当于一个变量

def convert(func, seq):
    return [func(each_num) for each_num in seq]

if __name__ == "__main__":
    myseq = (123, 45.67, 9999, -6.2e8)
    print(convert(int, myseq))
    print(convert(float, myseq))

# 输出结果
[123, 45, 9999, -620000000]
[123.0, 45.67, 9999.0, -620000000.0]

4, 内嵌函数

函数体内定义的函数,叫着内嵌函数, 内嵌函数的作用域仅在外部函数的函数体中

# bar是内嵌函数, 作用域仅在foo中
In [8]: def foo():
   ...:     def bar():
   ...:         print('bar() called')
   ...:     print('foo() called')
   ...:     bar()

4-1, 闭包

内嵌入函数定义中使用了外部函数的变量,这种内部函数就是闭包

这种由外部函数定义变量(不是全局作用域的变量),内部函数使用的变量叫着自由变量

函数的自由变量通过函数名.__closure__, 进行访问

output = '<int %r id=%#0x val=%d>'
w = x = y = z = 1

# f1是顶层函数,所以没有闭包变量
def f1():
    x = y = z = 2
    
    # f2中使用了外部变量x, 所以x是f2的闭包变量
    def f2():
        y = z = 3
        
        # f3中使用了外部变量x,y; 所以x, y是f3的闭包变量
        def f3():
            z = 4
            print(output % ('w', id(w), w))
            print(output % ('x', id(x), x))
            print(output % ('y', id(y), y))
            print(output % ('z', id(z), z))

        clo = f3.__closure__
        if clo:
            print('f3 closure vars:', [str(c) for c in clo])
        else:
            print("no f3 closure vars")

        f3()

    clo = f2.__closure__
    if clo:
        print('f2 closure vars:', [str(c) for c in clo])
    else:
        print("no f2 closure vars")
    f2()


if __name__ == "__main__":
    # 函数名.__closure__用来显示闭包变量的ID
    clo = f1.__closure__
    if clo:
        print('f1 closure vars:', [str(c) for c in clo])
    else:
        print("no f1 closure vars")
    f1()


# 输出结果
no f1 closure vars
f2 closure vars: ['<cell at 0x000001E1670683A0: int object at 0x00007FF83A1C06C0>']
f3 closure vars: ['<cell at 0x000001E1670683A0: int object at 0x00007FF83A1C06C0>', '<cell at 0x000001E167096FA0: int object at 0x00007FF83A1C06E0>']
<int 'w' id=0x7ff83a1c06a0 val=1>
<int 'x' id=0x7ff83a1c06c0 val=2>
<int 'y' id=0x7ff83a1c06e0 val=3>
<int 'z' id=0x7ff83a1c0700 val=4>

5, 装饰器

用来动态修改其他函数的功能, 也是函数,接收的函数作为参数

装饰器函数都返回的是wapper, 在最内层调用被装饰的函数

在wrapper上, 通过@functools.wrapper(函数名称), 可以起到被装饰的函数名不变

5-1, 无参装饰器

不带参数的装饰器函数,叫着无参装饰器

import functools
from time import ctime, sleep

# 定义装饰器
def deco(func):
    # @functools.wraps(func)作用是保持foo的函数名没有因为装饰的原因而被修改
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('%s %s' % (ctime(), func.__name__))
        return func(*args, **kw)

    return wrapper

# 使用deco来装饰foo
@deco
def foo():
    print('start ...')

if __name__ == "__main__":
    for i in range(2):
        sleep(1)
        # 调用foo(),相当于执行的是deco(foo)()
        foo()
    print('name:', foo.__name__)

# 输出结果
Wed Jul 26 13:59:38 2023 foo
start ...
Wed Jul 26 13:59:39 2023 foo
start ...
name: foo

5-2, 有参装饰器

带参数的装饰器函数,叫着有参装饰器

import functools
from time import ctime, sleep

# 定义装饰器
def deco2(option):
    def deco(func):
        # @functools.wraps(func)作用是保持foo的函数名没有因为装饰的原因而被修改
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s %s' % (option, func.__name__, ctime()))
            return func(*args, **kw)
        return wrapper
    return deco

# 使用deco2(option='begin')来装饰bar
@deco2(option='begin')
def bar():
    print('start ...')

if __name__ == "__main__":
    for i in range(2):
        sleep(1)
        # 调用bar(), 相当于执行deco2(option='begin')(bar)()
        bar()

# 输出结果
begin bar Wed Jul 26 14:02:24 2023
start ...
begin bar Wed Jul 26 14:02:25 2023
start ...

5-3, 多个装饰器

# 定义装饰器
def deco1(func):
    # @functools.wraps(func)作用是保持foo的函数名没有因为装饰的原因而被修改
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('%s %s' % (ctime(), func.__name__))
        return func(*args, **kw)

    return wrapper

# 定义装饰器
def deco2(option):
    def deco(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s %s' % (option, func.__name__, ctime()))
            return func(*args, **kw)
        return wrapper

    return deco

# 使用两个装饰器来装饰func
@deco2(option='begin')
@deco1
def func():
    print('now is %s' % func.__name__)

if __name__ == "__main__":
    # 调用func(),相当于执行deco2(option='begin')(deco1(func))()
    func()

# 输出结果
begin func Wed Jul 26 14:04:46 2023
Wed Jul 26 14:04:46 2023 func
now is func

6, lambda函数

通过f = lambda [args]: expression, 定义一个lambda函数, 并将其赋值给变量f, 通过f(args), 可获的expression的值

# 无参数lambda
In [1]: func = lambda: True

In [2]: func()
Out[2]: True

# 有参数的lambda
In [3]: func = lambda x, y: x + y

In [4]: func(1, 3)
Out[4]: 4

7, map函数

通过map(函数, 可迭代对象)方式, 可将函数依次作用到可迭代对象的每个元素,返回一个map对象,可通过list转换成列表

In [48]: map(str, range(10))
Out[48]: <map at 0x20df92d6080>

# map接收一个序列
In [49]: list(map(str, range(10)))
Out[49]: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

# map接收多个序列
# 此时lambda的参数数量, 与序列的个数一一对应
# 参数x对应第一个序列[1, 2, 3], 参数y对应第二个序列[1, 2, 3], 参数z对应第三个序列[1, 2, 3]
In [2]: list(map(lambda x, y, z: x+y+z, [1, 2, 3], [1, 2, 3], [1, 2, 3]))
Out[2]: [3, 6, 9]

8, reduce函数

python3中必须通过from functools import reduce导入, 来使用reduce

通过reduce(函数, 可迭代对象)函数接收可迭代对象前两个参数,把函数结果继续和序列的下一个元素传入到reduce计算,直到最后一个元素,返回最终的结果

In [4]: from functools import reduce

In [5]: reduce(lambda x, y: x+y, range(10))
Out[5]: 45

9, filter函数

通过filter(函数, 可迭代对象), 对可迭代对象的元素使用函数,过滤出结果为真的元素

返回的一个filter对象, 可通过list返回一个列表对象

In [9]: def odd(n):
   ...:     if n % 2 == 0:
   ...:         return n

# 返回的是filter对象
In [10]: filter(odd, range(10))
Out[10]: <filter at 0x2168666d4b0>

In [13]: a = filter(odd, range(10))

# 将filter转换为列表
In [14]: list(a)
Out[14]: [2, 4, 6, 8]

# 使用lambda简化代码
In [19]: list(filter(lambda n: n % 2==0, range(10)))
Out[19]: [0, 2, 4, 6, 8]

10, 偏函数

接收一个函数对象固定的参数值, 固定住函数的部分参数值,返回一个函数对象

需要通过from functools import partial, 才能使用偏函数

# 注意,固定住的参数是以关键字形式,不是位置参数
# 固定参数base=2
In [22]: int2 = partial(int, base=2)

# 在调用的时候不用传入固定的参数值
In [23]: int2('1111')
Out[23]: 15

# 也可以指定参数值传入
In [24]: int2('1111', base=10)
Out[24]: 1111

11, 变量的作用域

11-1, 全局变量

全局变量函数外定义的变量或者内建变量, 程序结束前都可以访问

11-2, 局部变量

局部变量函数中定义的变量,只能在函数中进行访问

11-3, 搜索标识符的顺序

先在局部名字空间查找再到全局名字空间,再到内建名字空间, 都没有找到的话就会报错

# global_str:全局变量 
In [40]: global_str = 'foo'

# local_str:局部变量
In [44]: def foo():
    ...:     local_str = 'bar'
    ...:     return global_str + local_str

In [45]: foo()
Out[45]: 'foobar'

11-4, global语句

在函数体内使用global语句,可以修改全局变量的值

In [55]: is_this_global = 'abc'

In [57]: def foo():
             # 使用global语句,修改全局变量的值
    ...:     global is_this_global
    ...:     this_is_loacl = 'aaa'
    ...:     is_this_global = 'bbb'
    ...:     return this_is_loacl + is_this_global

In [58]: foo()
Out[58]: 'aaabbb'

In [59]: is_this_global
Out[59]: 'bbb'

_________________________________________________________________________

def foo():
    global is_this_global
    this_is_loacl = 'aaa'
    # 注意,这里通过复合运算符修改时,必须在函数体内用global声明is_this_global
    is_this_global += 'bbb'
    return this_is_loacl + is_this_global


if __name__ == "__main__":
    print(foo())

12, 递归

函数体内对自身的调用,该函数就是递归函数

def factorial(n):
    if n in [0, 1]:
        return 1
    # 在函数内调用了函数本身
    return n * factorial(n-1)

if __name__ == "__main__":
    print(factorial(5))

13, 生成器

从语法上讲,是一个带yield的函数, 能暂停执行,并返回一个中间结果, 调用next()方法, 输出yield值,并从暂停的地方继续执行

# 定义生成器
In [11]: def simbal_gen():
    ...:     yield 1
    ...:     yield '2 ---> punch!'

# 通过for循环,输出生成器的元素
In [17]: for item in simbal_gen():
    ...:     print(item)
1
2 ---> punch!

def counter(start_at=0):
    count = start_at

    while True:
        # 若没有通过send发送值,则val=None
        # 若通过send发送值 ,则val=send发送的值
        val = yield count
        if val is not None:
            count = val
        else:
            count += 1

if __name__ == "__main__":
    count = counter(5)
    print(next(count))
    print(count.send(10))
    count.close()

14, 迭代器

迭代器为类序列对象创建了一个类序列接口,通过next()方法获取下一个元素, 通过for循环迭代输出所有的元素, 相比于序列对象性能得到了提升

14-1, 创建迭代器

14-1-1,iter(seq)

通过iter(seq), 将一个序列对象转换为迭代器

# 通过iter将序列'abcdef'转换成一个迭代器
In [11]: iter1 = iter(list('abcdef'))

In [12]: iter1
Out[12]: <list_iterator at 0x1c16d3ed0c0>

# 通过next(迭代器), 获取迭代器的下个元素
In [13]: next(iter1)
Out[13]: 'a'

# 通过for循环,遍历迭代器的所有元素
In [14]: for i in iter1:
    ...:     print(i)
    ...:
b
c
d
e
f

14-1-2, 定制迭代器(__iter__)

在类中,使用特殊方法__iter__, __next__, 可定制类为一个迭代器

class Fib(object):
    def __init__(self, num):
        self.num = num
        self.a, self.b = 0, 1
        self.count = 0
       
    # 返回self, 表示self本身是个迭代器
    def __iter__(self):
        return self
    # 通过next方法获取元素下个值
    def __next__(self):
        if self.count != 10:
            self.count += 1
            self.a, self.b = self.b, self.a + self.b
            return self.a
        else:
            # 注意,在不满住条件时, 必须用StopIteration抛出,不然会一直迭代
            raise StopIteration

if __name__ == "__main__":
    m = Fib(10)
    for i in m:
        print('i:', i)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值