一.流程控制
1.1 if 语句
可能会有零到多个 elif 部分,else 是可选的。关键字 ‘elif’ 是 ’else if’ 的缩写,这个可以有效地避免过深的缩进。if … elif … elif … 序列用于替代其它语言中的 switch
或 case
语句。
age = 60
if age >= 60:
print('老年人')
elif age >= 35:
print('中年人')
elif age >= 18:
print('青年')
else:
print('未成年人')
1.2 for 语句
Python 中的 for 语句和 C 或 Pascal 中的略有不同。通常的循环可能会依据一个等差数值步进过程(如 Pascal),或由用户来定义迭代步骤和中止条件(如 C ),Python 的 for 语句依据任意序列(链表或字符串)中的子项,按它们在序列中的顺序来进行迭代。例如(没有暗指):
words = ['cat', 'window', 'defenestrate']
# 倒序输出(切片的方式实现)
for w in words[::-1]: # 参数1:起始索引(默认值:0);参数2:终止索引(默认值:len - 1);
# 参数3:步长值为-1,表示反向获取(若为2则代表取2的倍数的值)
print(w)
1.3 while
n = 99
while n > 1:
print(n)
n -= 20
"""
while 循环使用 else 语句
如果else语句和while循环语句一起使用,则当条件变为 False 时,则执行else语句
"""
count = 0
while count < 5:
print(count, '小于5')
count += 1
else:
print(count, '大于或等于5')
1.4 range()
如果你需要一个数值序列,内置函数 range() 会很方便,它生成一个等差级数链表:
for i in range(5):
print(i) # 0 1 2 3 4
# 1.使用range指定区间的值
for i in range(3,5):
print(i) # 3 4
# 2.使range以指定数字开始并指定不同的增量(甚至可以是负数,有时这也叫做'步长')
for i in range(0,10,3):
print(i) # 0 3 6 9
# 3.结合range()和len()函数以遍历一个序列的索引
a = ['Google', 'Baidu', 'Runoob', 'Taobao']
for i in range(len(a)):
print(i, a[i]) # 0 Google 1 Baidu 2 Runoob 3 Taobao
# 4.使用内置 enumerate 函数进行遍历
sequence = [12, 34, 34, 23]
for i, j in enumerate(sequence):
print(i, j)
"""
0 12
1 34
2 34
3 23
"""
# 5.使用range()函数来创建一个列表
print(list(range(3))) # [0, 1, 2]
1.5 break
break 语句可以跳出 for 和 while 的循环体。如果你从 for 或 while 循环中终止,任何对应的循环 else 块将不执行
var = 10
while var > 0:
print ('当期变量值为 :', var)
var = var -1
if var == 5:
break # 若不处理判断逻辑,去掉此句会报错
1.6 continue
continue语句被用来告诉Python跳过当前循环块中的剩余语句,然后继续进行下一轮循环
var1 = 5
while var1 > 0:
var1 = var1 -1
if var1 == 2: # 变量为 2 时跳过输出
continue
print ('当前变量值 :', var1)
1.7 pass
- Python pass是空语句,是为了保持程序结构的完整性。
- pass 不做任何事情,一般用做占位语句。
- 如果没有内容,可以先写pass,但是如果不写pass,就会语法错误
b = 6
if b > 1:
pass # 必须要pass
# 示例如下
for letter in 'Runoob':
if letter == 'o':
pass # 此句要不要无所谓
print('执行 pass 块')
print('当前字母 :', letter)
"""
当前字母 : R
当前字母 : u
当前字母 : n
执行 pass 块
当前字母 : o
执行 pass 块
当前字母 : o
当前字母 : b
"""
二.函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()
。但你也可以自己创建函数,这被叫做用户自定义函数
2.1 自定义函数规则
- 函数代码块以
def
关键词开头,后接函数标识符名称和圆括号()
。 - 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
- return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
2.2 语法
Python 定义函数使用 def 关键字,默认情况下,参数值和参数名称是按函数声明中定义的的顺序匹配起来的。一般格式如下:
def 函数名(参数列表):
函数体
# 1.无参数的函数
def hello():
print('hello')
hello() # 调用函数hello(),输出结果:hello
# 2.函数中带上参数变量
def area(width, height):
return width * height
print(area(20,8)) # 调用函数area(),输出结果:160
2.3 参数
以下是调用函数时可使用的正式参数类型:
- 必需参数
- 关键字参数
- 默认参数
- 不定长参数
2.3.1 必需参数
必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。
调用printme()
函数,你必须传入一个参数,不然会出现语法错误:
#可写函数说明
def printme(str):
"""打印任何传入的字符串"""
print (str);
return;
#调用printme函数
printme();
"""
Traceback (most recent call last):
File "test.py", line 10, in <module>
printme();
TypeError: printme() missing 1 required positional argument: 'str'
"""
2.3.2 关键字参数
关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。
使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
#可写函数说明
def printme( str ):
"""打印任何传入的字符串"""
print (str);
return;
#调用printme函数
printme( str = "test") # test
# 2.以下实例中演示了函数参数的使用不需要使用指定顺序
#可写函数说明
def printinfo( name, age ):
"打印任何传入的字符串"
print ("名字: ", name);
print ("年龄: ", age);
return;
#调用printinfo函数
printinfo( age=50, name="jack" );
"""
名字: jack
年龄: 50
"""
2.3.3 默认参数
调用函数时,如果没有传递参数,则会使用默认参数。以下实例中如果没有传入 age 参数,则使用默认值:
#可写函数说明
def printinfo( name, age = 35 ):
"打印任何传入的字符串"
print ("名字: ", name);
print ("年龄: ", age);
return;
#调用printinfo函数
printinfo( age=50, name="jack" );
print ("------------------------")
printinfo( name="jack" );
"""
名字: runoob
年龄: 50
------------------------
名字: runoob
年龄: 35
"""
2.3.4 不定长参数
你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,和上述2种参数不同,声明时不会命名。基本语法如下:
def functionname([formal_args,] *var_args_tuple ):
"""函数_文档字符串"""
function_suite
return [expression]
-
*args
是可变参数,args接收的是一个tuple; -
**kw
是关键字参数,kw接收的是一个dict。 - 使用
*args
和**kw
是Python的习惯写法,当然也可以用其他参数名,但最好使用习惯用法 - 命名的关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值
- 定义命名的关键字参数在没有可变参数的情况下不要忘了写分隔符*,否则定义的将是位置参数。
- 在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数
加了星号(*
)的变量名会存放所有未命名的变量参数。如果在函数调用时没有指定参数,它就是一个空元组。我们也可以不向函数传递未命名的变量。
# 可写函数说明
def printinfo( arg1, *vartuple ):
"""打印任何传入的参数"""
print ("输出: ")
print (arg1)
for var in vartuple:
print (var)
return;
# 调用printinfo 函数
printinfo( 10 );
printinfo( 70, 60, 50 );
"""
输出:
10
输出:
70
60
50
"""
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
f1(1, 2, 3, 'a', 'b', x=99) # 调用
"""输出结果:a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}"""
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
f2(1, 2, d=99, ext=None) # 调用
"""输出结果:a = 1 b = 2 c = 0 d = 99 kw = {'ext': None} """
2.4 参数传递
在 python 中,类型属于对象,变量是没有类型的
a=[1,2,3]
a="Runoob"
以上代码中,[1,2,3] 是 List 类型,"Runoob" 是 String 类型,而变量 a 是没有类型,她仅仅是一个对象的引用(一个指针),可以是 List 类型对象,也可以指向 String 类型对象。
可更改(mutable)与不可更改(immutable)对象
在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。
不可变类型:变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变a的值,相当于新生成了a。
可变类型:变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。
python 函数的参数传递:
不可变类型: 类似 c++ 的值传递,如 整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在 fun(a)内部修改 a 的值,只是修改另一个复制的对象,不会影响 a 本身。
可变类型: 类似 c++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后fun外部的la也会受影响
python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。
# 1.python 传不可变对象实例
def ChangeInt( a ):
a = 10
b = 2
ChangeInt(b)
print(b) # 结果是 2
"""
实例中有 int 对象 2,指向它的变量是 b,在传递给 ChangeInt 函数时,按传值的方式复制了变
量 b,a 和 b 都指向了同一个 Int 对象,在 a=10 时,则新生成一个 int 值对象 10,并让 a
指向它
"""
# 2.python 传可变对象实例
# 可写函数说明
def changeme(mylist):
"""修改传入的列表"""
mylist.append([1,2,3,4]);
print ("函数内取值: ", mylist)
return
# 调用changeme函数
mylist = [10,20,30];
changeme( mylist );
print ("函数外取值: ", mylist)
# 传入函数的和在末尾添加新内容的对象用的是同一个引用。故输出结果如下:
"""
函数内取值: [10, 20, 30, [1, 2, 3, 4]]
函数外取值: [10, 20, 30, [1, 2, 3, 4]]
"""
2.5 匿名函数
2.5.1 匿名函数特点
python 使用 lambda
['læmdə]来创建匿名函数。
所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。
- lambda 只是一个表达式,函数体比 def 简单很多。
- lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
- lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
- 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
2.5.2 匿名函数语法
lambda 函数的语法只包含一个语句,如下:
lambda [arg1 [,arg2,.....argn]]:expression
2.5.3 匿名函数实例
# 可写函数说明
sum = lambda arg1, arg2: arg1 + arg2
# 调用sum函数
print ("相加后的值为 : ", sum( 10, 20 ))
print ("相加后的值为 : ", sum( 20, 20 ))
"""
相加后的值为 : 30
相加后的值为 : 40
"""
2.6 return语句
return [表达式] 语句用于退出函数,选择性地向调用方返回一个表达式。不带参数值的return语句返回None。之前的例子都没有示范如何返回数值,以下实例演示了 return 语句的用法:
# 可写函数说明
def sum( arg1, arg2 ):
# 返回2个参数的和."
total = arg1 + arg2
print ("函数内 : ", total)
return total;
# 调用sum函数
total = sum( 10, 20 );
print ("函数外 : ", total)
"""
函数内 : 30
函数外 : 30
"""
2.7 变量作用域
Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。
变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python的作用域一共有4种,分别是:
- L (Local) 局部作用域
- E (Enclosing) 闭包函数外的函数中
- G (Global) 全局作用域
- B (Built-in) 内建作用域
以 L –> E –> G –>B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。
x = int(2.9) # 内建作用域
g_count = 0 # 全局作用域
def outer():
o_count = 1 # 闭包函数外的函数中
def inner():
i_count = 2 # 局部作用域
Python 中只有模块(module
),类(class
)以及函数(def
、lambda
)才会引入新的作用域,其它的代码块(如 if/elif/else/
、try/except
、for/while
等)是不会引入新的作用域的,也就是说这这些语句内定义的变量,外部也可以访问。
实例中 msg 变量定义在 if 语句块中,但外部还是可以访问的。
如果将 msg 定义在函数中,则它就是局部变量,外部不能访问
>>> def test():
... msg_inner = 'I am from Runoob'
...
>>> msg_inner
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'msg_inner' is not defined
>>>
2.7.1 全局变量和局部变量
定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。
total = 0; # 这是一个全局变量
# 可写函数说明
def sum( arg1, arg2 ):
#返回2个参数的和
total = arg1 + arg2; # total在这里是局部变量.
print ("函数内是局部变量 : ", total)
return total;
#调用sum函数
sum( 10, 20 );
print ("函数外是全局变量 : ", total)
"""
函数内是局部变量 : 30
函数外是全局变量 : 0
"""
2.7.2 global 和 nonlocal关键字
当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了
num = 1
def fun1():
num = 100
fun1()
print(num)
"""
输出结果:1
为什么是1,而不是100?因为第一行num = 1中的num是全局变量,fun1()中的num是局部变量,函数执行完毕
就num会销毁,而print(num) 中的num却是全局变量,两个num不是一个变量,因此不会改变num的值。
"""
num = 1
def fun1():
global num # 需要使用 global 关键字声明
num = 100
fun1()
print(num) # 输出结果:100,在此段代码中num是全局变量且是同一个变量,因此值会被改变。
修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal
关键字了,如下实例:
x = 0
def outer():
x = 1
def inner():
x = 2
print("inner:", x)
inner()
print("outer:", x)
outer()
print("global:", x)
"""
inner: 2
outer: 1
global: 0
"""
x = 0
def outer():
x = 1
def inner():
nonlocal x # 需要使用 nonlocal 关键字声明
x = 2
print("inner:", x)
inner()
print("outer:", x)
outer()
print("global:", x)
"""
inner: 2
outer: 2
global: 0
"""
三.高级特性
3.1 切片
取一个list或tuple的部分元素是非常常见的操作。比如,一个list如下:
>>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
取前3个元素,应该怎么做?
笨办法:
>>> [L[0], L[1], L[2]]
['Michael', 'Sarah', 'Tracy']
之所以是笨办法是因为扩展一下,取前N个元素就没辙了。
取前N个元素,也就是索引为0-(N-1)的元素,可以用循环:
>>> r = []
>>> n = 3
>>> for i in range(n):
... r.append(L[i])
...
>>> r
['Michael', 'Sarah', 'Tracy']
3.2 迭代
如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为:迭代(Iteration)。
在Python中,迭代是通过for ... in
来完成的
- 判断一个对象是可迭代对象可通过collections模块的Iterable类型判断:
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
- 默认情况下,dict迭代的是key。如果要迭代value,可以用
for value in d.values()
,如果要同时迭代key和value,可以用for k, v in d.items()
。
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
... print(key)
...
a
c
b
- 字符串迭代:
>>> for ch in 'ABC':
... print(ch)
...
A
B
C
- list实现下标循环可用内置的
enumerate
函数可以把一个list变成索引元素对,这样就可以在for循环中同时迭代索引和元素本身:
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C
- for循环同时引用两个变量
>>> for x, y in [(1, 1), (2, 4), (3, 9)]:
... print(x, y)
...
1 1
2 4
3 9
3.3 列表生成式
列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。
如生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用list(range(1, 11))
:
>>> list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
如果要生成[1x1, 2x2, 3x3, ..., 10x10]怎么做?方法一是循环:
>>> L = []
>>> for x in range(1, 11):
... L.append(x * x)
...
>>> L
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 但是循环太繁琐,而列表生成式则可以用一行语句代替循环生成上面的list:
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
3.4 生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(generator)。
- 创建一个generator(方法一)
有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个generator:
>>> L = [x * x for x in range(3)]
>>> L
[0, 1, 4]
>>> g = (x * x for x in range(3))
>>> g
<generator object <genexpr> at 0x1022ef630>
# 可以通过next()函数获得generator的下一个返回值:
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
# for循环获得generator的下一个返回值
>>> g = (x * x for x in range(3))
>>> for n in g:
... print(n)
...
0
1
4
- yield关键字(方法二)
yield
是一个类似return
的关键字,只是这个函数返回的是个生成器,当你调用这个函数的时候,函数内部的代码并不立马执行 ,这个函数只是返回一个生成器对象,当使用for进行迭代的时候,函数中的代码才会执行
# 比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个
# 数相加得到:1, 1, 2, 3, 5, 8, 13, 21, 34, ...
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
>>> fib(6)
1
1
2
3
5
8
'done'
# 上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改
为yield b就可以了,这就是定义generator的另一种方法:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>
- generator的执行流程
函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)
# 调用该generator时,首先要生成一个generator对象,然后用next()函数不断获得下一个返回值
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
3.5 迭代器
集合数据类型,如list
、tuple
、dict
、set
、str
等和generator
,包括生成器和带yield
的generator function
。可以直接作用于for
循环的对象统称为可迭代对象:Iterable
。
- isinstance()判断一个对象是否是Iterable对象:
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
- 可被
next()
函数调用并不断返回下一个值的对象称为迭代器(Iterator)
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator,如:
>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False
- 把
list
、dict
、st
r等Iterable
变成Iterator
可以使用iter()
函数:
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
为什么list
、dict
、str
等数据类型不是Iterator
?
这是因为Python的
Iterator
对象表示的是一个数据流,Iterator
对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()
函数实现按需计算下一个数据,所以Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。而使用list
是永远不可能存储全体自然数的。