了解Python中的抽象(第一部分)

87 阅读5分钟

了解Python中的抽象性,第一部分

抽象是隐藏不必要的细节的艺术。它是一个基本的面向对象的编程概念,它允许我们只显示一个对象的相关特征,而隐藏其余部分。通过这样做,我们使我们的程序更加抽象,不做不必要的工作。在Python中,我们通过使用类和接口来实现这一点,在回顾了一些重要的基础知识之后,我们将在本系列的第二部分中讨论。

参数

参数是当函数被调用时被设置的变量。我们把形式上的参数称为我们在def 语句中写在函数名称后面的参数,而实际的参数(也被称为参数)是我们在调用函数时提供的值。

区分可变和不可变的数据结构是很重要的。可变的数据结构,如列表,可以改变参数;而字符串、数字和图元是不可变的,这意味着你不能修改它们,而只能用新的值替换它们。

通过抽象来消除复杂性的一个方便方法是使用函数来初始化数据结构。

例子。初始化一个简单的数据结构。

def init(data):
		data['full_name'] = {}
		data['phone'] = {}
		data['addr'] = {}
		data['email'] = {}

通过将初始化语句移到函数内,我们使代码更易读,而且松散耦合。

>>> customers = {}
>>> init(customers)
>>> customers
{'first': {}, 'last': {}, 'phone': {}, 'addr': {}}

此外,还有两种参数:位置参数和关键字参数。位置性参数只是那些位置重要的参数。当使用字典时,顺序可能不同,因为键本身没有特定的顺序。所以,在这些情况下,使用 Python 模块OrderedDict 是很有用的。

参数被保存在一个局部范围内,这给我们带来了另一个基本概念:作用域。

作用域

在 Python 中,一个名字空间是名字到对象的映射。作用域是指可以访问名字空间的文本位置。把命名空间看作是一种安全地组织变量和防止碰撞的方式,把作用域看作是一个与变量的生命周期相关的概念。

定义在函数内的变量(也称为函数或方法范围)是局部变量,通常在函数的生命周期内存在。它们在局部范围内,只能在该函数内被访问。在函数之外定义的变量是全局变量,可以在任何地方访问。这些变量在编译单元的生命周期内存在。

注意:Python没有像其他一些语言如Java或C#那样的块级范围。所有局部变量都被分配到函数作用域。

除了全局作用域之外,每个函数调用都会创建一个新的作用域。在 Python 中,函数可以在本地重新绑定一个变量,但是如果你在全局命名空间中声明同一个变量,这并不影响它在外层作用域中的值。换句话说,你在一个函数中访问一个全局变量是没有问题的,但是在一个函数中给一个变量赋值会自动使其成为局部变量。

如果一个局部变量或参数与你试图访问的全局变量的名字相同,那么全局变量将被局部变量所掩盖。在这种情况下,你可以使用函数globals (返回全局变量的字典)来获得对全局变量的访问。

例子。防止使用全局变量的阴影

>>> def join(parameter):
... 		print(parameter + globals()['parameter'])
...
>>> parameter = 'flower'
>>> combine('Sun')
Sunflower

嵌套作用域装饰器,也被称为 闭包,指的是将一个函数放在另一个函数里面。外层函数返回内层函数,但函数本身只被返回,不被调用。返回的函数带有环境和相关的局部变量。闭包实现了数据隐私,因为封闭的变量只在外层函数的范围内。它们也可以用一个实现特定接口的对象来代替,这将导致更加简洁。

递归

递归是一个简单的函数调用自己。一个以while True 开始并且不包含breakreturn statements 的循环将导致无限的递归。虽然理论上这应该永远运行下去,但每次函数被调用时,都会占用内存空间。因此,一段时间后,就没有空间了,程序以maximum recursion depth exceeded 结束。

为了使递归函数有用,它由两部分组成。

  1. 基本情况:解决尽可能小的问题并直接返回一个值
  2. 递归情况:对问题的较小部分进行一次或多次的递归调用

接下来,我们将看一个有递归和无递归的计算幂的例子。

例子。幂的计算

像内置函数pow (运算器)一样计算幂值

非递归方式编写幂函数(使用循环)

def power(x, n):
		result = 1
		for i in range(n):
				result *= x
		return result

将x提高到n的幂,相当于将一个数字乘以自己n-1倍。因此,power(2, 3)是2与自身相乘的两倍(2 x 2 x 2 = 8)

递归的方式来写一个幂函数

def power(x, n):
		if n == 0:
				return 1
		else:
				return x * power(x, n - 1)

基本情况:power(x, 0) = 1,适用于所有数字x

递归情况:n>0的power(x, n)是x与power(x, n-1)的乘积。

虽然我们可以用循环来解决这些问题,但递归很多时候更具有可读性,甚至可以通过适当的记忆来降低时间复杂度(保存后面计算中使用的结果的值)。

我们已经看到,参数、范围和递归是有效使用抽象的基础。在第二部分,我们将讨论如何在这个基础上用类和接口来设计大型功能单元。