跳转到内容

Python 编程/函数

来自维基教科书,开放的书籍,开放的世界


函数调用

[编辑 | 编辑源代码]

一个可调用对象是一个可以接受一些参数(也称为参数)并可能返回一个对象(通常是一个包含多个对象的元组)的对象。

函数是 Python 中最简单的可调用对象,但还有其他对象,例如或某些类实例。

定义函数

[编辑 | 编辑源代码]

函数在 Python 中通过以下格式定义

def functionname(arg1, arg2, ...):
    statement1
    statement2
    ...
>>> def functionname(arg1,arg2):
...     return arg1+arg2
...
>>> t = functionname(24,24) # Result: 48

如果函数不接受任何参数,它仍然必须包含括号,但括号中不包含任何内容

def functionname():
    statement1
    statement2
    ...

函数定义中的参数将函数调用(即调用函数时)传递的参数绑定到函数定义时给定的名称,这些名称称为形式参数。函数内部不知道实际参数的名称;实际参数的名称甚至可能不可访问(它们可能在另一个函数中)。

函数可以“返回”一个值,例如

def square(x):
    return x*x

函数可以在函数体内部定义变量,这些变量被认为是函数的“局部变量”。局部变量与参数一起构成函数范围内的所有变量。函数返回或到达函数体末尾时,函数中的任何名称都将被解除绑定。

您可以通过以下方式返回多个值

def first2items(list1):
  return list1[0], list1[1]
a, b = first2items(["Hello", "world", "hi", "universe"])
print(a + " " + b)

关键词:返回多个值,多个返回值。

声明参数

[编辑 | 编辑源代码]

当调用一个函数时,该函数需要一些值进行进一步处理,我们需要将一些值作为函数参数发送。例如

>>> def find_max(a,b):
   if(a > b):
      return str(a) + " is greater than " + str(b)
   elif(b > a):
      return str(b) + " is greater than " + str(a)
>>> find_max(30, 45)  #Here (30, 45) are the arguments passing for finding max between this two numbers
The output will be: 45 is greater than 30

默认参数值

[编辑 | 编辑源代码]

如果函数定义中的任何形式参数都以“arg = value”的格式声明,那么您在调用函数时可以选择不为这些参数指定值。如果您没有指定值,那么该参数将在函数执行时具有给定的默认值。

>>> def display_message(message, truncate_after=4):
...     print(message[:truncate_after])
...
>>> display_message("message")
mess
>>> display_message("message", 6)
messag

链接

可变长度参数列表

[编辑 | 编辑源代码]

Python 允许您声明两个特殊的参数,这些参数允许您创建任意长度的参数列表。这意味着每次您调用函数时,您都可以指定超过一定数量的任意数量的参数。

def function(first,second,*remaining):
    statement1
    statement2
    ...

当调用上述函数时,您必须为前两个参数提供值。但是,由于第三个参数用星号标记,因此前两个参数后的任何实际参数都将被打包成元组并绑定到“remaining”。

>>> def print_tail(first,*tail):
...     print(tail)
...
>>> print_tail(1, 5, 2, "omega")
(5, 2, 'omega')

如果我们声明一个以两个星号为前缀的形式参数,那么它将绑定到一个字典,该字典包含实际参数中与任何形式参数不对应的任何关键字参数。例如,考虑函数

def make_dictionary(max_length=10, **entries):
    return dict([(key, entries[key]) for i, key in enumerate(entries.keys()) if i < max_length])

如果我们用除 max_length 之外的任何关键字参数调用此函数,它们将被放入字典“entries”中。如果我们包含 max_length 的关键字参数,它将像往常一样绑定到形式参数 max_length。

>>> make_dictionary(max_length=2, key1=5, key2=7, key3=9)
{'key3': 9, 'key2': 7}

链接

按值传递和按引用传递

[编辑 | 编辑源代码]

作为参数传递给函数的对象是按引用传递的;它们没有被复制。因此,将大型列表作为参数传递不会涉及将所有成员复制到内存中的新位置。请注意,即使整数也是对象。但是,按值传递按引用传递在其他一些编程语言中存在的区别通常用于区分被调用函数是否可以实际更改传递的参数以及调用函数是否可以查看更改

传递的可变类型的对象(例如列表和字典)可以被被调用函数更改,并且更改对调用函数可见。传递的不可变类型的对象(例如整数和字符串)不能被被调用函数更改;调用函数可以确定被调用函数不会更改它们。对于可变性,另请参见数据类型章节。

一个例子

def appendItem(ilist, item):
  ilist.append(item) # Modifies ilist in a way visible to the caller

def replaceItems(ilist, newcontentlist):
  del ilist[:]                 # Modification visible to the caller
  ilist.extend(newcontentlist) # Modification visible to the caller
  ilist = [5, 6] # No outside effect; lets the local ilist point to a new list object,
                 # losing the reference to the list object passed as an argument
def clearSet(iset):
  iset.clear()

def tryToTouchAnInteger(iint):
  iint += 1 # No outside effect; lets the local iint to point to a new int object,
            # losing the reference to the int object passed as an argument
  print("iint inside:",iint) # 4 if iint was 3 on function entry 

list1 = [1, 2]
appendItem(list1, 3)
print(list1) # [1, 2, 3]
replaceItems(list1, [3, 4])
print(list1) # [3, 4]
set1 = set([1, 2])
clearSet(set1 )
print(set1) # set([])
int1 = 3
tryToTouchAnInteger(int1)
print(int1) # 3

阻止参数更改

[编辑 | 编辑源代码]

如果参数是不可变类型,对其进行的任何更改都将保留在被调用函数的本地范围内。但是,如果参数是可变类型,例如列表,对其进行的任何更改都将更新调用函数中的相应值。因此,如果调用函数想要确保传递给某个未知函数的可变值不会被更改,它必须创建并传递该值的副本。

一个例子

def evil_get_length(ilist):
  length = len(ilist)
  del ilist[:] # Muhaha: clear the list
  return length

list1 = [1, 2]
print(evil_get_length(list1[:])) # Pass a copy of list1
print(list1) # list1 = [1, 2]
print(evil_get_length(list1)) # list1 gets cleared
print(list1) # list1 = []

调用函数

[编辑 | 编辑源代码]

可以通过将括号中的参数附加到函数名称或将一对空括号附加到函数名称来调用函数(如果函数不接受任何参数)。

foo()
square(3)
bar(5, x)

可以通过将函数的返回值分配给一个变量来使用它,如下所示

x = foo()
y = bar(5,x)

如上所示,在调用函数时,您可以通过名称指定参数,并且您可以按任何顺序这样做

def display_message(message, start=0, end=4):
   print(message[start:end])

display_message("message", end=3)

上面的内容是有效的,start 将具有默认值 0。对此的限制是,在第一个命名参数之后,其后的所有参数也必须是命名参数。以下是无效的

display_message(end=5, start=1, "my message")

因为第三个参数(“my message”)是未命名的参数。

嵌套函数

[编辑 | 编辑源代码]

嵌套函数是在其他函数中定义的函数。任意级别的嵌套都是可能的。

嵌套函数可以读取在外部函数中声明的变量。对于可变的此类变量,嵌套函数甚至可以修改它们。对于不可变的此类变量(例如整数),在嵌套函数中尝试修改会导致 UnboundLocalError。在 Python 3 中,可以通过在嵌套函数中声明一个不可变的外部变量为 nonlocal,类似于 global。完成此操作后,嵌套函数可以为该变量分配一个新值,并且该修改将在嵌套函数之外可见。

嵌套函数可以在 #闭包 中使用,如下所示。此外,它们可以用来减少仅与单个函数相关的代码重复,通常由于看到外部变量而减少了参数列表。

修改列表(可变)外部变量的嵌套函数示例

def outside():
  outsideList = [1, 2]
  def nested():
    outsideList.append(3)
  nested()
  print(outsideList)

在嵌套函数定义下方访问外部变量的示例,仍然有效

def outside():
  def nested():
    outsideList.append(3)
  outsideList = [1, 2]
  nested()
  print(outsideList)

关键词:内部函数、局部函数。

链接

Lambda 表达式

[编辑 | 编辑源代码]

lambda 是一个匿名(无名)函数。它主要用于编写非常短的函数,这些函数以正常方式定义起来很麻烦。像这样的函数

>>> def add(a, b):
...    return a + b
...
>>> add(4, 3)
7

也可以使用 lambda 定义

>>> print ((lambda a, b: a + b)(4, 3))
7

lambda 通常用作其他函数的参数,这些函数需要函数对象,例如 sorted() 的 'key' 参数。

>>> sorted([[3, 4], [3, 5], [1, 2], [7, 3]], key=lambda x: x[1])
[[1, 2], [7, 3], [3, 4], [3, 5]]

lambda 形式通常用作闭包,例如以下示例所示

>>> def attribution(name):
...    return lambda x: x + ' -- ' + name
...
>>> pp = attribution('John')
>>> pp('Dinner is in the fridge')
'Dinner is in the fridge -- John'

请注意,lambda 函数可以使用创建它的 作用域 中变量的值,类似于上面描述的常规本地定义函数。事实上,导出由其构造函数体现的预先计算是闭包的重要实用程序之一。

链接

生成器函数

[编辑 | 编辑源代码]

在讨论循环时,您遇到了 迭代器 的概念。这依次生成某个序列的每个元素,而不是一次性生成整个序列,允许您处理比一次可以放入内存的序列大得多的序列。

您可以通过定义称为 生成器函数 的函数来创建自己的迭代器。为了说明它的用处,让我们从考虑一个简单的函数来返回两个列表的 连接 开始

def concat(a, b):
    return a + b

print(concat([5, 4, 3], ["a", "b", "c"]))
# prints [5, 4, 3, 'a', 'b', 'c']

想象一下想要做类似 concat(list(range(0, 1000000)), list(range(1000000, 2000000))) 的事情

这会起作用,但会消耗大量的内存。

考虑一个替代定义,它将两个迭代器作为参数

def concat(a, b):
    for i in a:
        yield i
    for i in b:
        yield i

注意使用 yield 语句而不是 return。我们现在可以使用类似这样的方法

for i in concat(range(0, 1000000), range(1000000, 2000000)):
    print(i)

并打印出大量数字,而根本不使用大量内存。

注意:您仍然可以在 Python 需要迭代器的地方(例如 concat 函数的参数)传递列表或其他序列类型;这仍然有效,并且可以让您不必担心在不需要的地方的区别。

链接

[编辑 | 编辑源代码]
华夏公益教科书