函数¶
- 我们已经见过一些函数的例子:例如:type()和print()
- 函数的调用形式:函数名(函数参数)
- 一个函数可以输入零个或多个参数,也可以返回零个或多个值
In [5]:
import math
rt2 = math.sqrt(2)
print(rt2)
1.4142135623730951
- Python的‘math’模块提供了许多数学函数。我们在使用这些函数之前,必须先导入该模块。然后使用’math.sqrt()’来调用’math’模块中的‘sqrt’函数。
- ‘math.sqrt()’接收一个参数,返回其平方根
函数¶
In [4]:
a = 2
b = 3
math.pow(a, b)
Out[4]:
8.0
- ‘math.pow()’接受两个参数,返回第一个参数的第二个参数次幂
Python math模块的文档:https://docs.python.org/zh-cn/3/library/math.html
定义函数¶
- 我们可以使用函数定义来创建新函数,这样我们就可以在需要时调用它。
In [7]:
# ‘def’关键字告诉Python,我们正在定义一个函数。
def return_multiple_values(text):
'''
函数的任何参数都在括号内给出。
冒号(:)用于表示缩进代码块的开始。
'''
value1 = 10 # 空白符可以是制表符或空格,只要保持一致即可。
value2 = text
value3 = [1, 2, 3]
return value1, value2, value3 # 使用‘return’关键字,定义函数的返回结果。
# 下一行不属于函数主体,通过缩进向Python传达这一事实。
result1, result2, result3 = return_multiple_values("hello")
print(result1)
print(result2)
print(result3)
10 hello [1, 2, 3]
注意:在R、C/C++和Java等语言中,代码块用大括号({})组织的。Python是以空白符号分隔的。
布尔表达式¶
布尔表达式用于判断语句的真伪。
‘bool’类型的变量可以是“True”或“False”。
Python中提供了一些比较运算符。
- 涉及比较运算符的表达式会返回一个布尔值。
In [ ]:
x, y = 20, 10
x == y # x is equal to y
x != y # x is not equal to y
x > y # x is strictly greater than y
x < y # x is strictly less than y
x >= y # x is greater than or equal to y
x <= y # x is less than or equal to y
布尔表达式¶
- 通过逻辑运算符‘and’、‘or’和‘not’将布尔表达式组合成更大的表达式。
In [10]:
x = 10
x < 20 and x > 0
Out[10]:
True
In [11]:
x > 100 and x > 0
Out[11]:
False
In [12]:
x > 100 or x > 0
Out[12]:
True
In [13]:
not x > 0
Out[13]:
False
条件执行¶
- 有时我们希望根据某些条件执行不同的操作,最简单的就是‘if’语句了。
In [16]:
def pos_neg_or_zero(x):
if x < 0: # 这个布尔表达式成为测试条件,或者简称为条件。
# 如果条件评估为“True”(真),那么Python将运行‘if’语句主体中的代码。
# 如果条件评估为‘False’(假),那么Python将跳过主体,并从‘if’语句末尾继续运行代码。
print('That is negative')
elif x == 0: # elif是‘else if’ 的缩写
print('That is zero.')
else: # 如果所有其他条件都不成立,我们将执行‘else’部分的代码块。
print('That is positive')
pos_neg_or_zero(1)
That is positive
In [17]:
pos_neg_or_zero(0)
pos_neg_or_zero(-100)
pos_neg_or_zero(20)
That is zero. That is negative That is positive
条件执行¶
注意:条件语句的主体可以包含任意数量的代码行,但必须至少有一行。如果不想执行任何操作,可以使用‘pass’关键字。
In [20]:
def thres_100(y):
if y > 100:
pass
if y < 100:
print('y is less than 100')
thres_100(200)
thres_100(0)
y is less than 100
递归¶
- 一个函数被允许调用它自己,这就是所谓的递归
In [24]:
def countdown(n):
if n <= 0:
print('We have lift off!')
else:
print(n)
countdown(n-1)
countdown(3)
3 2 1 We have lift off!
递归¶
- 稍作修改,函数将会无限递归
In [ ]:
def countdown(n):
if n <= 0:
print('We have lift off!')
else:
print(n)
countdown(n)
countdown(3)
迭代¶
- 递归是我们看到的第一个用于执行重复操作的工具。
- 但是有更好的工具来完成这项工作:while循环和for循环
In [26]:
a = 4
x = 3.5
epsilon = 10**-6
while True:
print(x)
y = (x + a/x)/2
if abs(x-y) < epsilon:
break # break关键字在被调用时终止当前循环。
x=y # update to our new estimate
3.5 2.321428571428571 2.022252747252747 2.000122433944265 2.0000000037472883
字符串¶
- 字符串是字符序列
In [2]:
animal = 'goat'
letter = animal[1]
letter
# Python序列从0开始计数
Out[2]:
'o'
In [8]:
'''
注意:在某些语言中,字符和长度为1的字符串是有区别的。
也就是说,字符‘g’和字符串“g”是不同的数据类型。
在Python中,不存在这样的区别。一个字符就是一个单字符字符串。
'''
animal[1]
Out[8]:
'o'
In [5]:
animal[2]
Out[5]:
'a'
字符串(迭代和遍历:for循环)¶
In [7]:
i = 0
while i < len(animal):
print(animal[i])
i = i + 1
g o a t
In [9]:
for c in animal:
print(c)
# For循环提供了一种简洁的方式来表达。
g o a t
字符串切片¶
- Python序列的一段成为切片。
In [10]:
s = "And now for something completely different"
# String[:m]选取从0开始到第(m-1)个字符的子序列。
s[:7]
Out[10]:
'And now'
In [11]:
# String[m :]选取从第m个字符开始到序列末尾的子序列。
s[22:]
Out[11]:
'completely different'
- 切片也适用于负数索引。
In [12]:
s[-20: -10]
Out[12]:
'completely'
In [13]:
s[-20:]
Out[13]:
'completely different'
字符串不可变¶
- 如何修改字符串中的单个字母?
In [14]:
mystr = "goat"
mystr[0] = 'b'
print(mystr)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[14], line 2 1 mystr = "goat" ----> 2 mystr[0] = 'b' 3 print(mystr) TypeError: 'str' object does not support item assignment
- 我们会得到一个错误,因为字符串是不可变的。不能更改已存在字符串的子序列。
In [15]:
mystr = "goat"
mystr = 'b' + mystr[1:]
mystr
Out[15]:
'boat'
字符串方法¶
- 字符串提供了许多有用的方法
- 更多Python中字符串的方法:https://docs.python.org/3/library/stdtypes.html#string-methods
In [16]:
mystr = "goat"
print(mystr.upper())
print('aBcDeFg'.lower())
GOAT abcdefg
- str.upper()使str中所有的字母变为大写。str.lower()类似。
In [17]:
'banana'.find('na')
# str.find(sub)查找str字符串中sub的第一个位置的索引。
Out[17]:
2
In [19]:
'goat'.startswith('go')
# 当str以sub开始则返回True
Out[19]:
True
列表¶
- 字符串是字符序列。在列表中,元素可以是任何类型。
In [ ]:
fruits = ['apple', 'orange', 'banana', 'kiwi']
fibonacci = [0, 1, 1, 2, 3, 5, 8, 13, 21]
mixed = ['one', 2, 3.0]
pythagoras = [[3, 4, 5], [5, 12, 13], [8, 15, 17]]
- 创建空列表的两种方式
x = []
x = list()
注意:与字符串不同,列表是可变的。我们可以在创建列表里后更改单个元素。
列表方法¶
- 列表提供了一组特定的方法:
In [20]:
# * list.append(x):将x添加到列表的末尾
animals = ['cat', 'dog', 'goat', 'bird']
animals.append('fish')
animals
Out[20]:
['cat', 'dog', 'goat', 'bird', 'fish']
In [21]:
# * list.append(x):将x添加到列表的末尾
fibonacci = [0, 1, 1, 2, 3, 5, 8]
fibonacci.append([13, 21])
fibonacci
Out[21]:
[0, 1, 1, 2, 3, 5, 8, [13, 21]]
In [22]:
# * list.sort():对列表中的元素进行排序
fibonacci = [0, 1, 1, 2, 3, 5, 8]
fibonacci.extend([13, 21])
fibonacci
Out[22]:
[0, 1, 1, 2, 3, 5, 8, 13, 21]
- list.remove(x):从列表中移除第一个等于x的元素
- list.pop():删除列表的最后一个元素并返回该元素
map, filter和reduce¶
- map:将函数(此处为capitalize_all)应用到序列的每个元素上。
In [23]:
def capitalize_all(t):
res = []
for s in t:
res.append(s.capitalize())
return res
capitalize_all(["sport", "great", "I"])
Out[23]:
['Sport', 'Great', 'I']
- filter:从列表中筛选部分元素,如函数only_upper函数。
In [24]:
def only_upper(t):
res = []
for s in t:
if s.isupper():
res.append(s)
return res
only_upper(["sport", "great", "I"])
Out[24]:
['I']
map, filter和reduce¶
- reduce:将一系列元素合为一个值的操作,如函数sum。
In [ ]:
t = [1, 2, 3]
sum(t)
- 大部分的列表操作都可以由map、filter以及reduce组合构成。
列表推导式¶
- 列表推导式是一种独特的数据处理方式,可以从一个数据序列构建另一个新的数据序列的结构体。
In [25]:
# [表达式 for 变量 in 列表]
[s.capitalize() for s in ['sport', 'great', "I"]]
Out[25]:
['Sport', 'Great', 'I']
In [26]:
# [表达式 for 变量 in 列表 if 条件]
[s for s in ['sport', 'great', 'I', 'Do'] if s.isupper()]
Out[26]:
['I']
列表和字符串¶
- 字符串是一系列的字符,列表是一系列的值,但是字符列表和字符串并不可等同视之。
In [27]:
s = 'spam'
print(s)
t = list(s)
print(t)
spam ['s', 'p', 'a', 'm']
列表和字符串¶
- 变量a和b都指向同一个字符串对象,然后变量al和bl指向不同的列表对象
In [28]:
a = 'banana'
b = 'banana'
print(a is b)
print(a == b)
True True
In [30]:
al = [1, 2, 3, 4]
bl = [1, 2, 3, 4]
print(al is bl)
print(al == bl)
False True
列表和字符串¶
- 有多个别称的对象。如果一个有别称的对象是可变的,那么一个别称所做的修改(不是重新赋值)会影响另一个。
In [32]:
al = [1, 2, 3, 4]
print(al)
bl = al
bl[0] = [1, 2]
print(al)
[1, 2, 3, 4] [[1, 2], 2, 3, 4]
字典和元组¶
- 字典
- 如同列表,但更加通用。列表中,索引必须是整数,而在字典中,索引可以使任何类型。
- 文档:https://docs.python.org/3/tutorial/datastructures.html#dictionaries
- 元组
- 元组和列表很像,但两者最大的区别在于元组不可变。
- 文档:https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
字典¶
字典包括:
- 一个索引集合,叫做键(keys)
- 一个值的集合
键和值的这种对应关系,叫做键值对
创建空字典的两种方式
In [4]:
a = {}
b = dict()
type(a), type(b)
Out[4]:
(dict, dict)
字典¶
- 创建和填充字典
In [5]:
umid2name = dict()
umid2name['aeinstein'] = 'Albert Einstein'
umid2name['kyfan'] = 'Ky Fan'
umid2name['aeinstein']
Out[5]:
'Albert Einstein'
In [7]:
umid2name['enoether'] = 'Amalie Emmy Noether'
umid2name['enoether']
Out[7]:
'Amalie Emmy Noether'
字典¶
In [8]:
# 用三个项创建一个新字典
eng2sp = {'one': 'uno', 'two': 'dos', 'three': 'trees'}
eng2sp
Out[8]:
{'one': 'uno', 'two': 'dos', 'three': 'trees'}
In [9]:
# 向已有字典中添加元素
eng2sp['four'] = 'you'
eng2sp
Out[9]:
{'one': 'uno', 'two': 'dos', 'three': 'trees', 'four': 'you'}
In [11]:
# 运算符in对字典也同样有效;它可以判断某个key是否存在于字典的键中
'one' in eng2sp, 'uno' in eng2sp
Out[11]:
(True, False)
命名空间¶
命名空间是执行代码的上下文
- “最外层”命名空间成为main
- 在命令行或Jupyter上运行代码,都是在__main__命名空间
- 在__main__空间中定义的变量都是全局变量
函数定义创建自己的本地名称空间
- 局部变量不能从它们的命名空间外部访问
- 在for循环/while循环中也有类似的规则
命名空间¶
- 例子:我们有一个模拟灯泡的程序灯泡状态由全局布尔变量lightbulb_on表示
In [13]:
lightbulb_on = False
def lights_on():
lightbulb_on = True
lights_on()
print(lightbulb_on)
False
In [14]:
lightbulb_on = False
def flip_switch():
lightbulb_on = not lightbulb_on
flip_switch()
--------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) Cell In[14], line 4 2 def flip_switch(): 3 lightbulb_on = not lightbulb_on ----> 4 flip_switch() Cell In[14], line 3, in flip_switch() 2 def flip_switch(): ----> 3 lightbulb_on = not lightbulb_on UnboundLocalError: cannot access local variable 'lightbulb_on' where it is not associated with a value
这段代码导致错误的事实是。默认情况下,Python将函数定义中的变量lightbulb_on视为与主命名空间中定义的lightbulb_on不同的变量。一般来说,这是一个很好的设计。它可以防止意外地更改全局状态信息。
In [16]:
lightbulb_on = False
def flip_switch():
global lightbulb_on
lightbulb_on = not lightbulb_on
flip_switch()
lightbulb_on
Out[16]:
True
命名空间¶
In [17]:
known = {0:0, 1:1}
def fibo(n):
if n in known:
return known[n]
res = fibo(n-1) + fibo(n-2)
known[n] = res
return res
fibo(30)
Out[17]:
832040
- 全局与局部的区别只对变量赋值重要。在fibo中我们没有执行任何变量赋值,所以不需要全局声明。与light_on相比,我们重新赋值lightbulb_on。默认情况下,变量赋值是局部的。
元组¶
- 元组和列表很像,最大的区别在于元组不可变。
In [19]:
a = ('A', 'B', 'C')
a[0] = 'D'
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[19], line 2 1 a = ('A', 'B', 'C') ----> 2 a[0] = 'D' TypeError: 'tuple' object does not support item assignment
- 创建元组(tuple)
In [18]:
a = ()
b = tuple()
c = 'A', 'B'
type(a), type(b), type(c)
Out[18]:
(tuple, tuple, tuple)
元组赋值¶
- 交换变量的值经常会用到. 比较传统的方法, 是使用中间变量. 比如下面例子, 互换 a 和 b 的值:
In [20]:
temp = a
a = b
b = temp
这种方法比较麻烦; 用元组赋值会优雅很多:
In [ ]:
a, b = b, a
右侧可以用各种类型的序列 (字符串, 列表或者元组). 例如, 将一个邮箱地址切分为用户名和域名, 可以这么写:
In [25]:
addr = 'monty@python.org'
uname, domain = addr.split('@')
addr.split('@'), uname, domain
Out[25]:
(['monty', 'python.org'], 'monty', 'python.org')
元组作为返回值¶
- 严格来讲, 函数只能返回一个值, 但是如果这个值是一个元组, 效果便等同于返回了多个值.比如, 你要将两个整数相除, 计算商和余数, 先计算 x//y , 再计算 x%y, 略显低效. 更好的方法是同时计算两个值.
In [26]:
t = divmod(7, 3)
t
Out[26]:
(2, 1)
- 自定义返回一个元组(多个值)的函数
In [ ]:
def min_max(t):
return min(t), max(t)
变长参数即元组¶
- 函数可以接收任意数量参数。以开头的参数会聚合*所有参数作为一个元组。比如,printall会接收任意数量的参数,并打印输出:
In [27]:
def printall(*args):
print(args)
printall(1, 2.0, '3')
(1, 2.0, '3')
- 聚合的对立面是扩展,如果你有一个值的序列,想将其作为多个参数,传入函数,那便可以使用*运算符。比如:divmod仅接收两个参数;如果传入一个元组,则运行异常:
In [28]:
t = (7, 3)
divmod(t)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[28], line 2 1 t = (7, 3) ----> 2 divmod(t) TypeError: divmod expected 2 arguments, got 1
变长参数即元组¶
In [29]:
divmod(*t)
Out[29]:
(2, 1)
- 很多内置函数都会用到变长参数元组。比如,max和min都可以接收任意数量的参数:
In [30]:
max(1, 2, 3)
Out[30]:
3
- 但sum函数不行。做个练习,写个函数sum_all,使其可以接收任意数量参数,并返回综合。
In [31]:
def sum_all(*args):
return sum(args)
sum_all(1, 2, 3)
Out[31]:
6
列表和元组¶
- zip是个内置函数,共接收两个或者更多序列作为参数,并交错获取其中元素。
In [4]:
s = 'abc'
t = [0, 1, 2]
x = zip(s, t)
next(x)
Out[4]:
('a', 0)
In [33]:
for pair in zip(s, t):
print(pair)
('a', 0) ('b', 1) ('c', 2)
- zip对象是一种迭代器。迭代器和列表在某些方面比较相似,但与列表不同的是,你不能从迭代器中通过索引获取元素
- 如果你想使用列表的运算符和方法,可以通过zip对象构建列表。
In [34]:
list(zip(s, t))
Out[34]:
[('a', 0), ('b', 1), ('c', 2)]
列表和元组¶
In [35]:
# 如果序列长度不同,则结果长度取决于较短的那个序列
list(zip('Anne', 'Elk'))
Out[35]:
[('A', 'E'), ('n', 'l'), ('n', 'k')]
- 如果结合zip, for和元组赋值,便可以同时遍历两个(或多个)序列
In [ ]:
def has_match(t1, t2):
for x, y in zip(t1, t2):
if x == y:
return True
return False
- 如果需要遍历序列元素以及索引,可以使用内置函数enumerate
In [36]:
for index, element in enumerate('abc'):
print(index, element)
0 a 1 b 2 c
字典和元组¶
- 字典有个方法叫做items,其返回一个元组序列,每个元组都是一个键值对
In [37]:
d = {'a': 0, 'b' : 1, 'c': 2}
d.items()
Out[37]:
dict_items([('a', 0), ('b', 1), ('c', 2)])
- dict_items对象是一个迭代键值对的迭代器
In [38]:
for key, value in d.items():
print(key, value)
a 0 b 1 c 2
- 将dict和zip结合使用,产生了一种新建字典的简洁方法
In [1]:
d = dict(zip([1, 2, 3], ['a', 'b', 'c']))
print(d)
d = dict(zip('abc', range(3)))
print(d)
{1: 'a', 2: 'b', 3: 'c'} {'a': 0, 'b': 1, 'c': 2}