广告
返回顶部
首页 > 资讯 > 后端开发 > Python >[Python] 函数与函数编程
  • 110
分享到

[Python] 函数与函数编程

函数Python 2023-01-31 01:01:33 110人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

1. 函数 使用def语句可定义函数: def add(x, y): return x + y 函数体就是在调用函数时所执行的一系列语句。调用函数的方法是在函数名称后面加上参数。参数的顺序必须与函数定义匹配,否则会引发TypeErr

1. 函数

使用def语句可定义函数:

def add(x, y):
    return x + y

函数体就是在调用函数时所执行的一系列语句。调用函数的方法是在函数名称后面加上参数。参数的顺序必须与函数定义匹配,否则会引发TypeError异常。可以为函数的参数设置默认值,例如:

def split(line, delimiter=','):
    statements

如果给最后一个参数名加上星号"*",函数就可以接受任意数量的参数:

def fprintf(file, fmt, *args):
    file.write(fmt % args)
fprintf(out, "%d %s %f", 42, "hello world", 3.45)

在这个例子中,所有余下的参数都作为一个元组放入args变量。要把元组args当作参数传递给函数,可以在函数调用中使用*args语法。例如:

def printf(fmt, *args):
    fprintf(sys.stdout, fmt, *args)

提供函数参数还有一种方式,即显示地命名每个参数并为其指定一个值,这称为关键字参数,例如:

def foo(w, x, y, z):
    statements
foo(x=3, y=22, w='hello', z=[1, 2])

使用关键字参数时,参数的顺序无关紧要。但除非提供了默认值,否则必须显式地命名所有必需的函数参数。位置参数和关键字参数可以同时使用,前提是所有位置参数必须先出现,给所有非可选参数提供值,例如:

foo('hello', 3, z=[1, 2], y=22)

如果函数定义的最后一个参数以"**"开头,可以把所有额外的关键字参数都放入一个字典中,并把这个字典传递给参数。例如:

def make_table(data, **params):
    fGColor = params.pop("fgcolor", "black")
    bgcolor = params.pop("bgcolor", "white")
    width = params.pop("width", None)
    if params:
        raise TypeError("Unsupported configuration options %s" % list(params))
make_table(items, fgcolor="black", bgcolor="white", border=1, borderstyle="grooved", cellpoadding=10, width=400)

关键字参数和可变长度参数列表可以一起使用,只要"**"参数出现在最后即可,例如:

def spam(*args, **kwargs):
    statements

 

2. 参数传递与返回值

调用函数时,函数参数仅仅是引用传入对象的名称。参数传递的基本语义和其他编程语言中已知的方式不完全相同,如“按值传递”和“按引用传递”。比如传递不可变的值,参数看起来实际是按值传递的,如果传递的是可变对象(如列表或字典)给函数,然后再修改此可变对象,这些改动将反映在原始对象中。例如:

a = [1, 2, 3, 4, 5]
def square(items):
    for i, x in enumerate(items):
        items[i] = x * x
square(a) # a = [1, 4, 9, 16, 25]

return语句从函数返回一个值。如果没有指定任何值或者省略return语句,就会返回None对象。如果返回值有多个,可以把它们放在一个元组中,例如:

def factor(a):
    d = 2
    while (d <= (a / 2)):
        if ((a / d) * d == a):
            return ((a / d), d)
        d = d + 1
    return (a, 1)

 

3. 作用域规则

每次执行一个函数时,就会创建新的局部命名空间。该命名空间代表一个局部环境,其中包含函数参数的名称和在函数体内赋值的变量名称。解析这些名称时,解释器将首先搜索局部命名空间。如果没有找到匹配的名称,它就会搜索全局命名空间。如果在全局命名空间中也找不到匹配值,最终会检查内置命名空间。如果仍然找不到,就会引发NameError异常。
命名空间的特性之一是在函数中对全局变量的操作,例如:

a = 42
def foo():
    a = 13
foo() # a仍然是42

执行这段代码时,尽量在函数foo中修改了变量a的值,但最终a仍然是42.在函数中对变量进行赋值时,这些变量始终绑定到该函数的局部命名空间中,因此函数体中的变量a引用的是一个包含值13的全新对象,而不是外部的变量。使用global语句可以改变这种行为,例如:

a = 42
def foo():
    global a
    a  = 13
foo() # a的值已变13

python支持嵌套的函数定义,例如:

def countdown(start):
    n = start
    def display():
        print('T-minus %d' % n)
    while n > 0:
        display()
        n -= 1

使用静态作用域绑定嵌套函数中的变量,即解析名称时首先检查局部作用域,而后由内向外一层层检查外部嵌套函数定义的作用域。如果找不到匹配,最后将搜索全局命名空间和内置命名空间。可以使用nonlocal语句绑定外部变量,例如:

def countdown(start):
    n = start
    def display():
        print('T-minus %d' % n)
    def decrement():
        nonlocal n
        n -= 1
    while n > 0:
        display()
        decrement()

nonlocal声明不会把名称绑定到任意函数中定义的局部变量,而是搜索当前调用栈中的下一层函数定义,即动态作用域。例如:

i = 0
def foo():
    i = i + 1 # UnboundLocalError异常

尽管有一个全局变量i,但它不会给局部变量i提供值。函数定义时就确定了变量是局部的还是全局的,而且在函数中不能突然改变它们的作用域。

 

4. 函数对象与闭包

函数在Python中是第一类对象。即可以把它们当作参数传递给其他函数,放在数据结构中,以及作为函数的返回结果。例如:

def callf(func):
    return func()

把函数当作数据处理时,它将显式地携带与定义该函数的周围环境相关的信息。这将影响到函数中自由变量的绑定方式。例如:

# foo.py
x = 42
def callf(func):
    return func()

# main.py
import foo
x = 37
def helloworld():
    reutrn "x is %d" % x
foo.callf(helloworld) # x is 37

在上例中,即使foo.py中也定义了一个变量x,变际调用的是与helloworld()函数相同的环境中定义的值。将组成函数的语句和这些语句的执行环境打包在一起时,得到的对象称为闭包。事实上所有函数都拥有一个指向了定义该函数的全局命名空间的__globals__属性。例如:

def page(url):
    def get():
        return urlopen(url).read()
    return get
python = page("Http://www.python.org")
jython = page("http://www.jython.org")
pydata = python() # 获取http://www.python.org
jydata = jython() # 获取http://www.jython.org

 

5. 装饰器

装饰器是一个函数,其主要用途是包装另一个函数或类。这种包装的首要目的是透明地修改或增强被包装对象的行为。表示装饰器的语法是特殊符号"@",例如:

@trace
def square(x):
    return x * x

上面的代码可以简化为:

def square(x):
    return x * x
square = trace(square)

现在考虑trace的实现:

enable_tracing =  True
if enable_tracing:
    debug_log = open("debug.log", "w")

def trace(func):
    if enable_tracing:
        def callf(*args, **kwargs):
            debug_log.write("Calling %s: %s, %s\n" % (func.__name__, args, kwargs))
            r = func(*args, **kwargs)
            debug_log.write("%s returned %s\n" % (func.__name__, r))
            return r
        return callf
    else:
        return func

这段代码中,trace()创建了写有一些调试输出的包装器函数,然后调用了原始函数对象。因此如果调用square()函数,看到的将是包装器中write()方法的输出。
使用装饰器时,它们必须出现在函数或类定义之前的单独行上。可以同时使用多个装饰器,例如:

@foo
@bar
@spam
def grok(x):
    pass\
grok = foo(bar(spam(grok)))

装饰器也可以接受参数,例如:

@eventhandler('BUTTON')
def handle_button(msg):
    ...
@eventhandler('RESET')
def handle_reset(msg):
    ...

如果提供参数,装饰器的语义如下所示:

def handle_button(msg):
    ...
temp = eventhandler('BUTTON')
handle_button = temp(handle_button)

对于类装饰器,应该让装饰器函数始终返回类对象作为结果。需要使用原始类定义的代码可能要直接引用类成员。
 

6. 生成器与yield

函数使用yield关键字可以定义生成器对象。生成器是一个函数,它生成一个值的序列,以便在迭代中使用,例如:

def countdown(n):
    while n > 0:
        yield n
        n -=1
    return

如果调用该函数,其中的代码不会开始执行,它会返回一个生成器对象,该对象在__next__()被调用,例如:

c = countdown(10)
c.__next__()

调用__next__()时,生成器函数将不断执行语句,直到遇到yield语句为止。通常不会在生成器上直接调用__next__()方法,而是在for语句、sum()或一些使用序列的其他操作中使用,例如:

for n in countdown(10):
    statements
a = sum(countdown(10))

生成器函数完成的标志是返回或引发StopIteration异常,这标志着迭代的结束。如果生成器没有全部完成,并且不再使用,可以调用close()方法,虽然通常情况下可以不必调用,例如:

c = countdown(10)
c.__next__()
c.close()
c.__next__() # 抛出异常

在生成器函数内部,在yield语句上出现GeneratorExit异常时就会调用close()方法。可以选择获取这个异常,例如:

def countdown(n):
    try:
        while n > 0:
            yield n
            n -= 1
    except GeneratorExit:
        print("Only made it to %d" % n)

 

7. 协程与yield表达式

在函数内,yield语句还可以用作出现在赋值运算符右边的表达式,例如:

def receiver():
    while True:
        n = (yield)
        print("Got %s" % n)

以这种方式使用yield语句的函数称为协程,它的执行是为了响应发送给它的值。它的行为也类似于生成器,例如:

r = receiver()
r.__next__()
r.send(1)
r.send(2)

在协程中需要首先调用__next__()这件事很容易被忘记,可以用一个自动完成该步骤的装饰器来包装协程,例如:

def coroutine(func):
    def start(*args, **kwargs):
        g = func(*args, **kwargs)
        g.next()
        return g
    return start

@coroutine
def receiver():
    while True:
        n = (yield)
        print("Got %s" % n)

r = receiver()
r.send("Hello World")

协程的运行一般是无限期的,除非它被显式关闭或者自己退出。使用close()可以关闭输入值的流,例如:

r.close()
r.send() # 抛出异常

关闭后如果继续给协程发送值,就会引发StopIteration异常,close()操作将在协程内部引发GeneratorExit异常。
 

8. 列表包含

函数的常用操作是将函数应用给一个列表的所有项,并使用结果创建一个新列表。这种操作很常见,因此出现了叫做列表推导的运算符,例如:

nums = [1, 2, 3, 4, 5]
squares = [n * n for n in nums]

列表推导的一般语法如下:

[expression for item1 in iterable1 if condition1
                                        for item2 in iterable2 if condition2
                                        ...
                                        for itemN in iterableN if conditionN]

下面给出一些例子:

a = [-3, 5, 2, -10, 7, 8]
b = 'abc'
c = [2 * s for s in a] # c = [-6, 10, 4, -20, 14, 16]
d = [s for s in a if s >= 0] # d = [5, 2, 7, 8]
e= [(x, y) for x in a
                                for y in b
                                if x > 0] 
# e = [(5, 'a'), (5, 'b'), (5, 'c'),
                    (2, 'a'), (2, 'b'), (2, 'c'),
                    (7, 'a'), (7, 'b'), (7, 'c'),
                    (8, 'a'), (8, 'b'), (8, 'c')]
f = [(1, 2), (3, 4), (5, 6)]
g  = [math.sqrt(x * x + y * y) for x, y in f] # g = [2.23606797749979, 5.0, 7.810249675906654]

 

9. 生成器表达式

生成器表达式是一个对象,它执行的计算与列表包含相同,但会迭代地生成结果,语法与列表包含相同,除了用圆括号代替方括号,如下:

(expression for item1 in iterable1 if condition1
                                        for item2 in iterable2 if condition2
                                        ...
                                        for itemN in iterableN if conditionN)

生成器表达式实际上不创建列表或者立即对圆括号内的表达式求值,它创建一个通过迭代并按照需要生成值的生成器对象,例如:

a  = [1, 2, 3, 4]
b = (10 * i for i in a)
print(b.__next__())
print(b.__next__())

使用列表推导时,Python实际上创建了包含结果数据的列表。而使用生成器表达式时,Python创建的是只知道如何按照需要生成数据的生成器。在某些应用中,可能影响性能和内存使用,例如:

f = open("data.txt")
lines = (t.strip() for t in f)
comments = (t for t in lines if t[0] == '#')
for c in comments:
    print(c)

生成器表达式不会创建序列形式的对象,不能对它进行索引。但是,使用内置的list()函数可以将生成器表达式转换为列表,例如:

clist = list(comments)

 

10. lambda运算符

使用lambda语句可以创建表达式形式的匿名函数:

lambda args: expression

args是以逗号分隔的参数列表,而expression是用到这些参数的表达式,例如:

a = lambda x, y: x + y
r = a(2, 3)

使用lambda语句定义的代码必须是合法的表达式。lambda语句中不能出现多条语句和其他非表达式语句,比如for或while。
 

11. 文档字符串

通常,函数的第一条语句会使用文档字符串,用于描述函数的用途,例如:

def factorial(n):
    """Computes n factorial. For examples:
            >>> factorial(6)
            120
    """
    if n <= 1: return 1
    else: return n* factorial(n-1)

文档字符串保存在函数的__doc__属性中,IDE通常使用该函数提供交互式帮助。如果需要使用装饰器,可能会破坏与文档字符串相关的帮助功能,例如:

def wrap(func):
    call(*args, **kwargs):
        return func(*args, **kwargs)
    return call
@wrap
def factorial(n):
    """Computes n factorial."""

如果查目的地以上函数的帮助,可能会看到一个相当奇怪的内容,解决方法是编写可以传递函数名称和文档字符串的装饰器函数,例如:

def wrap(func):
    call(*args, **kwargs):
        return func(*args, **kwargs)
    call.__doc__ =  func.__doc__
    call.__name__ = func.__name__
    return call

因为这是一个常见问题,所以functools模块提供了函数wraps,用于自动复制这些属性,例如:

from functools import wraps
def wrap(func):
    @wrap(func)
    call(*args, **kwargs):
        return func(*args, **kwargs)
    return call

 

12. 函数属性

可以给函数添加任意属性,例如:

def foo():
    statements
foo.secure = 1
foo.private = 1

函数属性保存在函数的__dict__属性中,__dic__属性是一个字典。和文档字符串一样,也要注意混合使用函数属性和装饰器的问题。如果使用装饰器包装函数,实际上是由装饰器函数而非原始函数来访问属性。
 

13. eval()、exec()和compile()函数

eval(str [, globals [, locals]])函数执行一个表达式字符串并返回结果,例如:

a = eval('3 * math.sin(3.5 + x) + 7.2')

相似地,exec(str [, globals [, locals]])函数执行一个包含任意Python代码的字符串。例如:

a = [3, 5, 10, 13]
exec("for i in a: print(i)")

这两个函数都会在调用者的命名空间中执行。eval()和exec()函数可以接受一个或两个可选的映射对象,分别用作代码执行的全局和局部命名空间,例如:

globals = {'x': 7, 'y': 10, 'birds': ['Parrot', 'Swallow', 'Albatross']}
locals = {}
a = eval("3 * x + 4 * y", globals, locals)
exec("fro b in birds: print(b)", globals, locals)

compile(str, filename, kind)函数将字符串编译为字节码,其中str是包含要编译代码的字符串,而filename是定义该字符串的文件,kind参数指定了要编译代码的类型。single表示一条语句,exec代表一组语句,而eval代表一个表达式。例如:

s = "for i inrange(0, 10): print(i)"
c = compile(s, '', 'exec')
exec(c)
s2 = "3 * x + 4 * y"
c2 = compile(s2, '', 'eval')
result = eval(c2)

--结束END--

本文标题: [Python] 函数与函数编程

本文链接: https://www.lsjlt.com/news/183701.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • [Python] 函数与函数编程
    1. 函数 使用def语句可定义函数: def add(x, y): return x + y 函数体就是在调用函数时所执行的一系列语句。调用函数的方法是在函数名称后面加上参数。参数的顺序必须与函数定义匹配,否则会引发TypeErr...
    99+
    2023-01-31
    函数 Python
  • 浅谈Python函数式编程的返回函数与匿名函数
    目录返回函数匿名函数返回函数 所谓返回函数,顾名思义,就是把函数作为返回值。高阶函数除了可以将函数作为参数之外,还可以将函数作为结果进行返回。下面来实现一个可变参数的连乘,求积函数可...
    99+
    2023-05-15
    Python函数 Python函数式 Python返回函数 Python匿名函数
  • python 函数编程
    1、函数是逻辑结构化和过程化的一种编程方法 python中函数定义使用关键字def来定义 def test(x):  "the function definitions"      x+=1      return x def:定义函数的...
    99+
    2023-01-30
    函数 python
  • Python函数式编程的返回函数与匿名函数怎么定义
    本篇内容介绍了“Python函数式编程的返回函数与匿名函数怎么定义”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!返回函数所谓返回函数,顾名思...
    99+
    2023-07-06
  • Python编程:递归与匿名函数及函数属性与文档字符串(函数补充)
    本文简单扼要地说,辅以代码进一步地加深理解。 递归函数当函数调用自身而生成最终结果时,这样的函数称为递归。有时递归函数非常有用,因为它们使编写代码变得更容易——使用递归范式编写一些算法非常容易,而其他算法则不是这样。没有不能以迭代方式重写的...
    99+
    2023-05-14
    Python 语言 递归函数
  • Python 函数式编程
    lambda表达式 以lambda开头 紧跟一定的参数(如果有的话) 参数后用冒号和表达式主题隔开 只是一个表达式,所以,没有return # 计算一个数字的100倍数 stm = lambda x: 100 * x stm(89...
    99+
    2023-01-30
    函数 Python
  • 简析Python函数式编程字符串和元组及函数分类与高阶函数
    目录函数式编程中的字符串不变类型元组普通元组的声明与访问函数式的分类any() 、all() 、len()、sum() 对比学习zip()、reversed()、enumerate(...
    99+
    2022-11-12
  • 【Kotlin】函数式编程 ② ( 过滤函数 | predicate 谓词函数 | filter 过滤函数 | 合并函数 | zip 函数 | folder 函数 | 函数式编程意义 )
    文章目录 一、过滤函数二、filter 函数原型三、filter 过滤函数代码示例1、filter 函数简单示例2、filter 过滤函数与 flatMap 变换函数 组合使用示例3、filter 过滤函数与 map 变换函数 组合...
    99+
    2023-08-19
    kotlin 函数式编程 过滤函数 合并函数 谓词函数
  • 怎么用Python函数编译函数
    本篇内容介绍了“怎么用Python函数编译函数”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!直接使用findall ( rule , tar...
    99+
    2023-06-17
  • python编程之函数思想
    #!/usr/bin/python ##Filename: game.py from sys import exit from random import randint def death():   quips = ["1-dead"...
    99+
    2023-01-31
    函数 思想 python
  • 【Kotlin】函数式编程 ① ( 函数式编程简介 | 高阶函数 | 函数类别 | Transform 变换函数 | 过滤函数 | 合并函数 | map 变换函数 | flatMap 变换函数 )
    文章目录 一、函数式编程简介1、编程范式2、高阶函数3、函数式编程4、前端开发技术 二、函数类别三、变换函数四、map 变换函数1、map 函数原型分析2、map 函数设计理念3、代码示例 五、flatMap 变换函数1、f...
    99+
    2023-08-19
    kotlin 函数式编程 变换函数 map flatMap
  • Python函数式编程之返回函数实例详解
    目录看代码:用filter函数来计算素数用Python高阶函数来实现这个算法:高阶函数实现打印小于100的素数:总结 高阶函数除了可以接受函数作为参数外,还可以把函数作为结...
    99+
    2022-11-11
  • 过程与函数
    过程与函数(另外还有包与触发器)是命名的PL/SQL块(也是用户的方案对象),被编译后存储在数据库中,以备执行。因此,其它PL/SQL块可以按名称来使用他们。所以,可以将商业逻辑、企业规则写成函数或过程保存...
    99+
    2022-10-18
  • 关于Python中的 oct 函数与 min 函数
    一.Python oct 函数简介 oct 函数将一个整数转换成 8 进制字符串,语法如下: ''' 参数: x – 整数; 返回值:返回整数对应的八进制数据; '''...
    99+
    2022-11-12
  • python中format函数与round函数的区别
    目录前言不同之处round函数进位原则示例format函数示例总结前言 在对数据进行处理时我们常常会用到format与round函数。二者都能保留若干位小数,但在处理过程上稍有不同。...
    99+
    2022-11-12
  • 详解python编程slice与indices函数用法示例
    一般来说,内置的slice()函数会创建一个切片对象,可以用在任何允许进行切片操作的地方。 下面是slice的简介: # slice 两种用法 class slice(stop)...
    99+
    2022-11-12
  • Python函数式编程是什么
    这篇文章将为大家详细讲解有关Python函数式编程是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。函数范式在命令式范式中,通过为计算机提供一系列指令然后执行它们来完成任务。在执行这些指令时,可以改变某...
    99+
    2023-06-29
  • 什么是python函数式编程
    python函数式编程是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的。但python对函数式编程只提供部分支持,且python中允许使用变量,所以python不是不...
    99+
    2022-10-07
  • Python-函数式编程介绍 (上)
    1、代码的组织结构不清晰,可读性差 2、实现重复的功能时,只能重复编写实现功能的代码,代码繁多,耗费时间精力 3、假如需要部分功能的扩展或者更新时,需要找出所有实现此功能的地方,一一修改,无...
    99+
    2023-01-31
    函数 Python
  • Python函数式编程之闭包
    -------------------------函数式编程之*******闭包------------------------ Note: 一:简介 函数式编程不是程序必须要的,但是对于简化程序有很重要的作用。 Python...
    99+
    2023-01-30
    函数 Python
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作