typora-copy-images-to: Risk Management and Financial Institution
typora-copy-images-to: Python 编程导论
文章目录
Python 编程导论 Chapter 4 函数、作用域与抽象
- 函数的作用是为了代码的通用性
- 使Python可以更容易地扩展和重用代码
4.1 函数与作用域
4.1.1 函数定义
def name of function (list of formal parameters):
body of function
def maxVal(x,y) :
if x > y:
return x
else:
return y
# x,y 在本例中是形式参数
# return 语句只能用在函数体中。
当函数被调用的时候,执行如下过程:
1. 构成实参的表达式被求值
2. 执行点从调用点转到函数体的第一条语句
3. 执行函数体中的代码,直到遇到return语句,此时,return后面的表达式就是此次调用函数的值,或者没有语句可以继续执行,此时函数返回值None
4. 这次函数调用的值就是返回值
5. 执行点移动到紧跟在这次函数调用后面的代码
# 实际练习:编写一个函数isIn,接受两个字符串作为参数,如果一个字符串是另一个字符串
# 的一部分,返回True,否则返回False。提示:你可以使用内置的str类型的操作符in。
def isin(x,y):
if str(x) in str(y):
return True
else:
return False
print(isin('name','names'))
4.1.2 关键字参数和默认值
- 两种方式可以将形参绑定到实参
- 位置参数
- 关键字参数
def printName(firstName, lastName, reverse = False):
if reverse:
print(f'{lastName} , {firstName}')
else:
print(firstName, lastName)
# ,但将关键字参数放在非关键字参数后面是不合法的
# 如:printName('Olga', lastName = 'Puchmajerova', False)
# 关键值经常与默认参数值结合使用
4.1.3 作用域
def f(x): #name x used as formal parameter
y = 1
x = x + y
print('x =', x)
return x
x = 3
y = 2
z = f(x) #value of x used as actual parameter
print('z =', z)
print('x =', x)
print('y =', y)
# 每一个函数都定义了一个命名空间,也称为作用域
函数体中的赋值语句x = x + y将局部名称x绑定到对象4。f中的赋值语句根本不会影响f作用域之外的名称x和y的绑定
- 作用域的理解:
- 最顶层,shell层,用来记录这一层所有名称定义和它们当前的绑定
- 调用函数时,建立新的符号表(栈帧),记录函数中所有名称定义,如果函数中又调用了一个函数 ,就再建立一个栈帧
- 函数结束,栈帧随之消失
def f(x):
def g():
x = 'abc'
print('x =', x)
def h():
z = x
print('z =', z)
x = x + 1
print('x =', x)
h()
g()
print('x =', x)
return g
x = 3
z = f(x)
print('x =', x)
print('z =', z)
z()
输出:
x = 4
z = 4
x = abc
x = 4
x = 3
z = <function f.<locals>.g at 0x1092a7510>
x = abc

- 引用名称时,顺序并不重要。只要在函数体内任何地方有对象与名称进行绑定(即使在名称作为赋值语句左侧项之前,就已经出现在某个表达式中),就认为这个名称是函数的局部变量
def f():
print(x)
def g():
print(x)
x = 1
x = 3
f()
x = 3
g()
# 调用函数g时,出现UnboundLocalError: local variable 'x' referenced before assignment的错误
4.2 规范
def findRoot(x, power, epsilon):
"""x和epsilon是整数或者浮点数,power是整数
epsilon>0 且power>=1
如果y**power和x的差小于epsilon,就返回浮点数y,
否则返回None"""
if x < 0 and power%2 == 0: #Negative number has no even-powered
#roots
return None
low = min(-1.0, x)
high = max(1.0, x)
ans = (high + low)/2.0
while abs(ans**power - x) >= epsilon:
if ans**power < x:
low = ans
else:
high = ans
ans = (high + low)/2.0
return ans
def testFindRoot(): # 测试代码
epsilon = 0.0001
for x in [0.25, -0.25, 2, -2, 8, -8]:
for power in range(1, 4):
print('Testing x =', str(x), 'and power = ', power)
result = findRoot(x, power, epsilon)
if result == None:
print(' No root')
else:
print(' ', result**power, '~=', x)
- 函数的规范定义了函数编写者与使用者之间的约定。我们将函数使用者称为客户。可以认为约定包括以下两部分
- 假设:客户使用函数必须满足的前提条件,通常是对实参的限制
- 保证:函数实现的功能
4.3 递归
-
递归是一种描述性方法
-
递归定义包括两部分:
- 一种基本情形可以直接得出某种特定情形的结果
- 一种递归情形(归纳情形),定义了该问题再其它情形下的结果
-
例如:
- 任何在美国境内出生的儿童;
- 在美国境外出生的婚生儿童,父母是美国公民,并且双亲之一在孩子出生前在美国居住过;
- 在美国境外出生的婚生儿童,双亲之一是美国公民,他(她)在孩子出生前至少在美国居住5年,且其中至少2年是在其14岁生日之后
# 阶乘的递归实现
def factR(n):
""""假设n是正整数
返回n!"""
if n == 1:
return n
else:
return n*factR(n - 1)
# 通过在factR函数体内调用factR,递归终究会在factR(1)时结束
# 阶乘的迭代实现
def factI(n):
"""假设n是正整数
返回 n!"""
result = 1
while n > 1:
result = result * n
n -= 1
return result
4.3.1 斐波那契数列
# 斐波那契数列的递归实现
def fib(n):
"""假定n是正整数
返回第n个斐波那契数"""
if n == 0 or n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
def testFib(n):
for i in range(n+1):
print('fib of', i, '=', fib(i))
- 找到某种抽象方式表示问题的解,常常是编程过程中最困
难的部分
4.3.2 回文
- 递归经常用于非数值的问题中
# 定义函数 isPalinrome,检查一个字符串在顺读和倒读时是否一致
# 其中包含两个辅助函数 toChars 和 isPal
toChars 函数将所有字母转换为小写,并且移除所有非字母字符
isPal 使用递归完成实际工作
def isPalindrome(s):
"""假设s是字符串
如果s是回文字符串则返回True,否则返回False。
忽略标点符号、空格和大小写。"""
def toChars(s):
s = s.lower()
letters = ''
for c in s:
if c in 'abcdefghijklmnopqrstuvwxyz':
letters = letters + c
return letters
def isPal(s):
if len(s) <= 1:
return True
else:
return s[0] == s[-1] and isPal(s[1:-1])
return isPal(toChars(s))
当两个布尔值表达式通过and连接时,每个表达式被称为合取项。如果它们是通过or连接的,那么被称为分取项
4.4 全局变量
- 定义:告诉Python,某一名称是定义在代码所在函数外层的模块作用域中的,而不是代码所在函数的作用域中的,否则就会被认为是局部变量
def fib(x):
"""假设x是正整数
返回第x个斐波那契数"""
global numFibCalls
numFibCalls += 1
if x == 0 or x == 1:
return 1
else:
return fib(x-1) + fib(x-2)
def testFib(n):
for i in range(n+1):
global numFibCalls
numFibCalls = 0
print('fib of', i, '=', fib(i))
print('fib called', numFibCalls, 'times.')
4.5 模块
- 多人合作编写同一程序,那么试图更新同一个文件时,会很困难
# 创建一个circle.py的 文件
pi = 3.14159
def area(radius):
return pi*(radius**2)
def circumference(radius):
return 2*pi*radius
def sphereSurface(radius):
return 4.0*area(radius)
def sphereVolume(radius):
return (4.0/3.0)*pi*(radius**3)
- 模块通常保存在单独的文件中
- 乍看上去,使用点标记法有些麻烦。但换个角度想想,当我们导入一个模块时,根本不知道
这个模块在实现时使用了哪些局部名称。使用点标记法可以充分限定变量名,避免名称冲突造成程序损害的可能性
4.6 文件
- Python通过文件句柄处理文件,实现了操作系统的独立性
nameHandle = open('kids', 'w')
nameHandle.write('Michael\n')
nameHandle.write('Mark\n')
nameHandle.close()
nameHandle = open('kids', 'r')
for line in nameHandle:
print(line[:-1])
nameHandle.close()
# 打开文件的方式参数,“a”追加,“w”写入,“r”读取