开始 Python 之旅
脚本文件
chmod +x helloworld.py
./helloworld.py
如果程序中没有 #!/usr/bin/env python3 的话,应该使用 python3 helloworld.py 来执行,否则使用 ./helloworld.py 程序会被当作 bash 脚本来执行,最终报错。
代码风格建议
使用 4 个空格来缩进
永远不要混用空格和制表符
在函数之间空一行
在类之间空两行
字典,列表,元组以及参数列表中,在 , 后添加一个空格。对于字典,: 后面也添加一个空格
在赋值运算符和比较运算符周围要有空格(参数列表中除外),但是括号里则不加空格:a = f(1, 2) + g(3, 4)
模块
模块是包含了我们能复用的代码的文件,包含了不同的函数定义,变量。模块文件通常以 .py 为扩展名。
Python 本身在默认安装时就带有大量的模块。我们之后将会用到其中的一部分。在使用模块前先导入它。
变量和数据类型
关键字和标识符
下列的标识符是 Python3 的关键字,并且不能用于通常的标识符。关键字必须完全按照下面拼写:
False def if raise
None del import return
True elif in try
and else is while
as except lambda with
assert finally nonlocal yield
break for not
class from or
continue global pass
help()
keywords
从键盘读取输入
通常情况下,Python 的代码中是不需要从键盘读取输入的。不过我们还是可以在 Python 中使用函数 input() 来做到这一点,input() 有一个用于打印在屏幕上的可选字符串参数,返回用户输入的字符串。
从键盘读取一个数字并且检查这个数字是否小于 100。
#!/usr/bin/env python3
number = int(input("Enter an integer: "))
if number <= 100:
print("Your number is less than or equal to 100")
else:
print("Your number is greater than 100")
while 是使用一个表达式作为判断的条件,如果条件不能够达成则停止循环:
w = 20
while w > 1:
print(w)
w -= 1
计算投资:
#!/usr/bin/env python3
amount = float(input("Enter amount: ")) # 输入数额
inrate = float(input("Enter Interest rate: ")) # 输入利率
period = int(input("Enter period: ")) # 输入期限
value = 0
year = 1
while year <= period:
value = amount + (inrate * amount)
print("Year {} Rs. {:.2f}".format(year, value))
amount = value
year = year + 1
Year {} Rs. {:.2f}".format(year, value) 称为字符串格式化,大括号和其中的字符会被替换成传入 str.format() 的参数,也即 year 和 value。其中 {:.2f} 的意思是替换为 2 位精度的浮点数。
求 N 个数字的平均值
#!/usr/bin/env python3
N = 10
sum = 0
count = 0
print("please input 10 numbers:")
while count < N:
number = float(input())
sum = sum + number
count = count + 1
average = sum / N
print("N = {}, Sum = {}".format(N, sum))
print("Average = {:.2f}".format(average))
温度转换
使用公式 C = (F - 32) / 1.8 将华氏温度转为摄氏温度。
#!/usr/bin/env python3
fahrenheit = 0
print("Fahrenheit Celsius")
while fahrenheit <= 250:
celsius = (fahrenheit - 32) / 1.8 # 转换为摄氏度
print("{:5d} {:7.2f}".format(fahrenheit , celsius))
fahrenheit = fahrenheit + 25
{:5d} 的意思是替换为 5 个字符宽度的整数,宽度不足则使用空格填充。 {:7.2f}的意思是替换为为 7 个字符宽度的保留两位的小数,小数点也算一个宽度,宽度不足则使用空格填充。其中7指宽度为 7,.2f指保留两位小数。
单行定义多个变量或赋值
a, b = 45, 54
a, b = b, a
data = ("shiyanlou", "China", "Python")
name, country, language = data
运算符和表达式
运算符
只要有任意一个操作数是浮点数,结果就会是浮点数。
进行除法运算时若是除不尽,结果将会是小数,这很自然,如果要进行整除,使用 // 运算符,它将返回商的整数部分。
#!/usr/bin/env python3
days = int(input("Enter days: "))
months = days // 30
days = days % 30
print("Months = {} Days = {}".format(months, days))
#!/usr/bin/env python3
days = int(input("Enter days: "))
print("Months = {} Days = {}".format(*divmod(days, 30)))
关系运算符
逻辑运算符
对于逻辑 与,或,非,我们使用 and,or,not 这几个关键字。
逻辑运算符 and 和 or 也称作短路运算符:它们的参数从左向右解析,一旦结果可以确定就停止。例如,如果 A 和 C 为真而 B 为假,A and B and C 不会解析 C 。作用于一个普通的非逻辑值时,短路运算符的返回值通常是能够最先确定结果的那个操作数。
关系运算可以通过逻辑运算符 and 和 or 组合,比较的结果可以用 not 来取反意。逻辑运算符的优先级又低于关系运算符,在它们之中,not 具有最高的优先级,or 优先级最低,所以 A and not B or C 等于 (A and (notB)) or C。当然,括号也可以用于比较表达式。
>>> 5 and 4 # 首先判断5,肯定为true,那么最终的结果就取决于 and 后面那个的布尔值,4 的布尔值为 true,这样就可以确定整个表达式的值为 true 了,所以返回 4
4
>>> 0 and 4 # 首先判断0,因为 0 的布尔值为 false,那么不管 and 后面那个的布尔值是什么,整个表达式的布尔值都应该为 false 了,这个时候就不需要判断 4 了,直接返回最先确定结果的那个数也就是0
0
>>> False or 3 or 0
3
>>> 2 > 1 and not 3 > 5 or 4
True
简写运算符
x op= expression 为简写运算的语法形式。其等价于 x = x op expression
#!/usr/bin/env python3
N = 100
a = 2
while a < N:
print(str(a))
a *= a
表达式
通常我们书写表达式的时候,会在每一个运算符左右都放一个空格
#!/usr/bin/env python3
a = 9
b = 12
c = 3
x = a - b / 3 + c * 2 - 1
y = a - b / (3 + c) * (2 - 1)
z = a - (b / (3 + c) * 2) - 1
print("X = ", x)
print("Y = ", y)
print("Z = ", z)
类型转换
计算数列 1/x+1/(x+1)+1/(x+2)+ ... +1/n,我们设 x = 1,n = 10。
#!/usr/bin/env python3
sum = 0
for i in range(1, 11):
sum += 1.0 / i
print("{:2d} {:6.4f}".format(i , sum))
求解二次方程式:
#!/usr/bin/env python3
import math
a = int(input("Enter value of a: "))
b = int(input("Enter value of b: "))
c = int(input("Enter value of c: "))
d = b * b - 4 * a * c
if d < 0:
print("ROOTS are imaginary")
else:
root1 = (-b + math.sqrt(d)) / (2 * a)
root2 = (-b - math.sqrt(d)) / (2 * a)
print("Root 1 = ", root1)
print("Root 2 = ", root2)
计算一位数码相机销售人员的工资。他的基本工资是 1500,每售出一台相机他可以得到 200 并且获得 2% 的抽成。程序要求输入相机数量及单价。
#!/usr/bin/env python3
basic_salary = 1500
bonus_rate = 200
commission_rate = 0.02
numberofcamera = int(input("Enter the number of inputs sold: "))
price = float(input("Enter the price of camera: "))
bonus = (bonus_rate * numberofcamera)
commission = (commission_rate * price * numberofcamera)
print("Bonus = {:6.2f}".format(bonus))
print("Commission = {:6.2f}".format(commission))
print("Gross salary = {:6.2f}".format(basic_salary + bonus + commission))
圆的面积
不要使用 input 等方法获得输入,程序不需要输入任何参数,直接输出半径为 2 的圆的面积:
import math
# 计算圆的面积
area = 2 * 2 * math.pi
# 格式化输出圆的面积,保留10位小数
print("{:.10f}".format(area))
控制流 If-else
接受用户输入的一个数并且检查这个数是否小于 100。
#!/usr/bin/env python3
number = int(input("Enter a number: "))
if number < 100:
print("The number is less than 100")
如果输入数大于 100 则打印 "Greater than"。
#!/usr/bin/env python3
number = int(input("Enter a number: "))
if number < 100:
print("The number is less than 100")
else:
print("The number is greater than 100")
>>> x = int(input("Please enter an integer: "))
>>> if x < 0:
... x = 0
... print('Negative changed to zero')
... elif x == 0:
... print('Zero')
... elif x == 1:
... print('Single')
... else:
... print('More')
真值检测
if x:
pass
循环
while 循环
>>> n = 0
>>> while n < 11:
... print(n)
... n += 1
斐波那契(Fibonacci)数列
这个数列前两项为 1,之后的每一个项都是前两项之和。所以这个数列看起来就像这样: 1,1,2,3,5,8,13,...。
#!/usr/bin/env python3
a, b = 0, 1
while b < 100:
print(b)
a, b = b, a + b
#!/usr/bin/env python3
a, b = 0, 1
while b < 100:
print(b, end=' ')
a, b = b, a + b
print()
幂级数
e^x = 1 + x + x^2 / 2! + x^3 / 3! + ... + x^n / n! (0 < x < 1)。
#!/usr/bin/env python3
x = float(input("Enter the value of x: "))
n = term = num = 1
result = 1.0
while n <= 100:
term *= x / n
result += term
n += 1
if term < 0.0001:
break
print("No of Times= {} and Sum= {}".format(n, result))
乘法表
打印 10 以内的乘法表。
#!/usr/bin/env python3
i = 1
print("-" * 50)
while i < 11:
n = 1
while n <= 10:
print("{:5d}".format(i * n), end=' ')
n += 1
print()
i += 1
print("-" * 50)
>>> 's' * 10
'ssssssssss'
>>> print("*" * 10)
**********
>>> print("#" * 20)
####################
>>> print("--" * 20)
----------------------------------------
>>> print("-" * 40)
----------------------------------------
打印星号的例子
#!/usr/bin/env python3
row = int(input("Enter the number of rows: "))
n = row
while n >= 0:
x = "*" * n
print(x)
n -= 1
#!/usr/bin/env python3
n = int(input("Enter the number of rows: "))
i = 1
while i <= n:
print("*" * i)
i += 1
#!/usr/bin/env python3
row = int(input("Enter the number of rows: "))
n = row
while n >= 0:
x = "*" * n
y = " " * (row - n)
print(y + x)
n -= 1
列表
切片并不会改变正在操作的列表,切片操作返回其子列表,这意味着下面的切片操作返回列表一个新的(栈)拷贝副本:
>>> a[:]
[1, 342, 223, 'India', 'Fedora']
切片时的索引是在两个元素之间 。左边第一个元素的索引为 0,而长度为 n 的列表其最后一个元素的右界索引为 n。
Python 中有关下标的集合都满足左闭右开原则,切片中也是如此,也就是说集合左边界值能取到,右边界值不能取到。
一个过大的索引值(即大于列表实际长度)将被列表实际长度所代替,当上边界比下边界大时(即切片左值大于右值)就返回空列表。
列表也支持连接这样的操作,它返回一个新的列表
>>> a + [36, 49, 64, 81, 100]
[1, 342, 223, 'India', 'Fedora', 36, 49, 64, 81, 100]
列表允许修改元素:
>>> cubes = [1, 8, 27, 65, 125]
>>> cubes[3] = 64
>>> cubes
[1, 8, 27, 64, 125]
也可以对切片赋值,此操作可以改变列表的尺寸,或清空它:
>>> letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> letters
['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> # 替换某些值
>>> letters[2:5] = ['C', 'D', 'E']
>>> letters
['a', 'b', 'C', 'D', 'E', 'f', 'g']
>>> # 现在移除他们
>>> letters[2:5] = []
>>> letters
['a', 'b', 'f', 'g']
>>> # 通过替换所有元素为空列表来清空这个列表
>>> letters[:] = []
>>> letters
[]
要检查某个值是否存在于列表中:
>>> a = ['ShiYanLou', 'is', 'cool']
>>> 'cool' in a
True
>>> 'Linux' in a
False
要检查列表是否为空
if list_name: # 列表不为空
pass
else: # 列表为空
pass
列表是允许嵌套的(创建一个包含其它列表的列表)
>>> a = ['a', 'b', 'c']
>>> n = [1, 2, 3]
>>> x = [a, n]
>>> x
[['a', 'b', 'c'], [1, 2, 3]]
>>> x[0]
['a', 'b', 'c']
>>> x[0][1]
'b'
for 循环
>>> a = ['ShiYanLou', 'is', 'powerful']
>>> for x in a:
... print(x)
>>> a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> for x in a[::2]:
... print(x)
如果你需要一个数值序列,内置函数 range() 会很方便,它生成一个等差数列(并不是列表):
>>> for i in range(5):
... print(i)
...
0
1
2
3
4
>>> range(1, 5)
range(1, 5)
>>> list(range(1, 5))
[1, 2, 3, 4]
>>> list(range(1, 15, 3))
[1, 4, 7, 10, 13]
>>> list(range(4, 15, 2))
[4, 6, 8, 10, 12, 14]
continue 语句
如同 break ,我们可以在循环中使用另一个语句 continue。它会跳过其后的代码回到循环开始处执行。这意味着它可以帮助你跳过部分循环。
#!/usr/bin/env python3
while True:
n = int(input("Please enter an Integer: "))
if n < 0:
continue # 这会返回到循环开始处执行
elif n == 0:
break
print("Square is ", n ** 2)
print("Goodbye")
循环的 else 语句
>>> for i in range(0, 5):
... print(i)
... else:
... print("Bye bye")
棍子游戏
这里有 21 根棍子,首先用户选 1 到 4 根棍子,然后电脑选 1 到 4 根棍子。谁选到最后一根棍子谁就输。判断一下用户有赢的机会吗?如果没有的话,如何修改游戏规则可以使用户有赢的机会呢?
特别说明:用户和电脑一次选的棍子总数只能是 5。
#!/usr/bin/env python3
sticks = 21
print("There are 21 sticks, you can take 1-4 number of sticks at a time.")
print("Whoever will take the last stick will lose")
while True:
print("Sticks left: " , sticks)
if sticks == 1:
print("You took the last stick, you lose")
break
sticks_taken = int(input("Take sticks(1-4):"))
if sticks_taken >= 5 or sticks_taken <= 0:
print("Wrong choice")
continue
print("Computer took: " , (5 - sticks_taken) , "\n")
sticks -= 5
数据结构
列表
>>> a = [23, 45, 1, -3434, 43624356, 234]
>>> a.append(45)
>>> a
[23, 45, 1, -3434, 43624356, 234, 45]
>>> a.insert(0, 1) # 在列表索引 0 位置添加元素 1
>>> a
[1, 23, 45, 1, -3434, 43624356, 234, 45]
>>> a.insert(0, 111) # 在列表索引 0 位置添加元素 111
>>> a
[111, 1, 23, 45, 1, -3434, 43624356, 234, 45]
列表方法 count(s) 会返回列表元素中 s 的数量
>>> a.count(45)
2
>>> a.remove(234)
>>> a
[111, 1, 23, 45, 1, -3434, 43624356, 45]
>>> a.reverse()
>>> a
[45, 43624356, -3434, 1, 45, 23, 1, 111]
>>> b = [45, 56, 90]
>>> a.extend(b) # 添加 b 的元素而不是 b 本身
>>> a
[45, 43624356, -3434, 1, 45, 23, 1, 111, 45, 56, 90]
>>> a.sort()
>>> a
[-3434, 1, 1, 23, 45, 45, 45, 56, 90, 111, 43624356]
>>> del a[-1]
>>> a
[-3434, 1, 1, 23, 45, 45, 45, 56, 90, 111]
栈是一种 LIFO (Last In First Out 后进先出)数据结构。
>>> a = [1, 2, 3, 4, 5, 6]
>>> a
[1, 2, 3, 4, 5, 6]
>>> a.pop()
6
>>> a.pop()
5
>>> a.pop()
4
>>> a.pop()
3
>>> a
[1, 2]
>>> a.append(34)
>>> a
[1, 2, 34]
队列 是一种在末尾追加数据以及在开始弹出数据的数据结构,它是 FIFO (First In First Out 先进先出)的数据结构。
>>> a = [1, 2, 3, 4, 5]
>>> a.append(1)
>>> a
[1, 2, 3, 4, 5, 1]
>>> a.pop(0)
1
>>> a.pop(0)
2
>>> a
[3, 4, 5, 1]
>>> squares = []
>>> for x in range(10):
... squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
这个 for 循环中的被创建(或被重写)的名为 x 的变量在循环完毕后依然存在。
squares = list(map(lambda x: x**2, range(10)))
等价于
squares = [x**2 for x in range(10)]
列表推导式由包含一个表达式的中括号组成,表达式后面跟随一个 for 子句,之后可以有零或多个 for 或 if 子句。结果是一个列表,由表达式依据其后面的 for 和 if 子句上下文计算而来的结果构成。
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
>>> combs = []
>>> for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
>>> a=[1,2,3]
>>> z = [x + 1 for x in [x ** 2 for x in a]]
>>> z
[2, 5, 10]
元组
>>> a = 'Fedora', 'ShiYanLou', 'Kubuntu', 'Pardus'
>>> a
('Fedora', 'ShiYanLou', 'Kubuntu', 'Pardus')
>>> a[1]
'ShiYanLou'
>>> for x in a:
... print(x, end=' ')
...
Fedora ShiYanLou Kubuntu Pardus
>>> divmod(15,2)
(7, 1)
>>> x, y = divmod(15,2)
>>> x
7
>>> y
1
>>> a = (123)
>>> a
123
>>> type(a)
>>> a = (123, )
>>> b = 321,
>>> a
(123,)
>>> b
(321,)
>>> type(a)
>>> type(b)
>>> type(len)
集合
集合是一个无序不重复元素的集。基本功能包括关系测试和消除重复元素。集合对象还支持 union(联合),intersection(交),difference(差)和 symmetric difference(对称差集)等数学运算。
大括号或 set() 函数可以用来创建集合。注意:想要创建空集合,你必须使用 set() 而不是 {}。后者用于创建空字典。
>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket) # 你可以看到重复的元素被去除
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket
True
>>> 'crabgrass' in basket
False
>>> # 演示对两个单词中的字母进行集合操作
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a # a 去重后的字母
{'a', 'r', 'b', 'c', 'd'}
>>> a - b # a 有而 b 没有的字母
{'r', 'd', 'b'}
>>> a | b # 存在于 a 或 b 的字母
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b # a 和 b 都有的字母
{'a', 'c'}
>>> a ^ b # 存在于 a 或 b 但不同时存在的字母
{'r', 'd', 'b', 'm', 'z', 'l'}
>>> a = {'a','e','h','g'}
>>> a.pop() # pop 方法随机删除一个元素并打印
'h'
>>> a.add('c')
>>> a
{'c', 'e', 'g', 'a'}
字典
字典是是无序的键值对(key:value)集合,同一个字典内的键必须是互不相同的。一对大括号 {} 创建一个空字典。初始化字典时,在大括号内放置一组逗号分隔的键:值对,这也是字典输出的方式。
>>> data = {'kushal':'Fedora', 'kart_':'Debian', 'Jace':'Mac'}
>>> data
{'kushal': 'Fedora', 'Jace': 'Mac', 'kart_': 'Debian'}
>>> data['kart_']
'Debian'
>>> data['parthan'] = 'Ubuntu'
>>> data
{'kushal': 'Fedora', 'Jace': 'Mac', 'kart_': 'Debian', 'parthan': 'Ubuntu'}
>>> del data['kushal']
>>> data
{'Jace': 'Mac', 'kart_': 'Debian', 'parthan': 'Ubuntu'
>>> 'ShiYanLou' in data
False
>>> dict((('Indian','Delhi'),('Bangladesh','Dhaka')))
{'Indian': 'Delhi', 'Bangladesh': 'Dhaka'}
>>> data
{'Kushal': 'Fedora', 'Jace': 'Mac', 'kart_': 'Debian', 'parthan': 'Ubuntu'}
>>> for x, y in data.items():
... print("{} uses {}".format(x, y))
...
Kushal uses Fedora
Jace uses Mac
kart_ uses Debian
parthan uses Ubuntu
>>> data = {}
>>> data.setdefault('names', []).append('Ruby')
>>> data
{'names': ['Ruby']}
>>> data.setdefault('names', []).append('Python')
>>> data
{'names': ['Ruby', 'Python']}
>>> data.setdefault('names', []).append('C')
>>> data
{'names': ['Ruby', 'Python', 'C']}
>>> data.get('foo', 0)
0
>>> for i, j in enumerate(['a', 'b', 'c']):
... print(i, j)
...
0 a
1 b
2 c
>>> a = ['Pradeepto', 'Kushal']
>>> b = ['OpenSUSE', 'Fedora']
>>> for x, y in zip(a, b):
... print("{} uses {}".format(x, y))
...
Pradeepto uses OpenSUSE
Kushal uses Fedora
判断学生成绩是否达标的程序,要求输入学生数量,以及各个学生物理、数学、历史三科的成绩,如果总成绩小于 120,程序打印 “failed”,否则打印 “passed”。
#!/usr/bin/env python3
n = int(input("Enter the number of students: "))
data = {} # 用来存储数据的字典变量
Subjects = ('Physics', 'Maths', 'History') # 所有科目
for i in range(0, n):
name = input('Enter the name of the student {}: '.format(i + 1)) # 获得学生名称
marks = []
for x in Subjects:
marks.append(int(input('Enter marks of {}: '.format(x)))) # 获得每一科的分数
data[name] = marks
for x, y in data.items():
total = sum(y)
print("{}'s total marks {}".format(x, total))
if total < 120:
print(x, "failed :(")
else:
print(x, "passed :)")
计算两个矩阵的 Hadamard 乘积。要求输入矩阵的行/列数(在这里假设我们使用的是 n × n 的矩阵)。
#!/usr/bin/env python3
n = int(input("Enter the value of n: "))
print("Enter values for the Matrix A")
a = []
for i in range(n):
a.append([int(x) for x in input().split()])
print("Enter values for the Matrix B")
b = []
for i in range(n):
b.append([int(x) for x in input().split()])
c = []
for i in range(n):
c.append([a[i][j] * b[i][j] for j in range(n)])
print("After matrix multiplication")
print("-" * 7 * n)
for x in c:
for y in x:
print(str(y).rjust(5), end=' ')
print()
print("-" * 7 * n)
字符串
>>> s = "I am Chinese"
>>> s
'I am Chinese'
>>> s = 'I am Chinese'
>>> s = "Here is a line \
... split in two lines"
>>> s
'Here is a line split in two lines'
>>> s = "Here is a line \n split in two lines"
>>> s
'Here is a line \n split in two lines'
>>> print(s)
Here is a line
split in two lines
>>> print("""\
... Usage: thingy [OPTIONS]
... -h Display this usage message
... -H hostname Hostname to connect to
... """)
Usage: thingy [OPTIONS]
-h Display this usage message
-H hostname Hostname to connect to
>>> s = "shi yan lou"
>>> s.title()
'Shi Yan Lou'
>>> z = s.upper()
>>> z
'SHI YAN LOU'
>>> z.lower()
'shi yan lou'
>>> s = "I am A pRoGraMMer"
>> s.swapcase()
'i AM a PrOgRAmmER'
>>> s = "jdwb 2323bjb"
>>> s.isalnum()
False
>>> s = "jdwb2323bjb"
>>> s.isalnum()
True
>>> s = "SankarshanSir"
>>> s.isalpha()
True
>>> s = "Sankarshan Sir"
>>> s.isalpha()
False
>>> s = "1234"
>>> s.isdigit() # 检查字符串是否所有字符为数字
True
>>> s = "ShiYanLou is coming"
>>> s.islower() # 检查字符串是否所有字符为小写
False
>>> s = "Shiyanlou Is Coming"
>>> s.istitle() # To 检查字符串是否为标题样式
True
>>> s = "CHINA"
>>> s.isupper() # 检查字符串是否所有字符为大写
True
>>> s = "We all love Python"
>>> s.split()
['We', 'all', 'love', 'Python']
>>> x = "shiyanlou:is:waiting"
>>> x.split(':')
['shiyanlou', 'is', 'waiting']
>>> "-".join("GNU/Linux is great".split())
'GNU/Linux-is-great'
>>> s = " a bc\n "
>>> s.strip()
'a bc'
>>> s = "www.foss.in"
>>> s.lstrip("cwsd.") #删除在字符串左边出现的'c','w','s','d','.'字符
'foss.in'
>>> s.rstrip("cnwdi.") #删除在字符串右边出现的'c','n','w','d','i','.'字符
'www.foss'
>>> s = "faulty for a reason"
>>> s.find("for")
7
>>> s.find("fora")
-1
>>> s.startswith("fa") # 检查字符串是否以 fa 开头
True
>>> s.endswith("reason") # 检查字符串是否以 reason 结尾
True
find() 能帮助你找到第一个匹配的子字符串,没有找到则返回 -1。
回文是一种无论从左还是从右读都一样的字符序列。比如 “madam”。
#!/usr/bin/env python3
s = input("Please enter a string: ")
z = s[::-1] #把输入的字符串s 进行倒序处理形成新的字符串z
if s == z:
print("The string is a palindrome")
else:
print("The string is not a palindrome")
对用户输入的一行文本进行单词计数
#!/usr/bin/env python3
s = input("Enter a line: ")
print("The number of words in the line are %d" % (len(s.split(" "))))
函数
编写一个函数来检查给出的字符串是否为回文,然后返回 True 或者 False。
#!/usr/bin/env python3
def palindrome(s):
return s == s[::-1]
if __name__ == '__main__':
s = input("Enter a string: ")
if palindrome(s):
print("Yay a palindrome")
else:
print("Oh no, not a palindrome")
在函数内部和函数调用的代码中都使用同一个变量 a
#!/usr/bin/env python3
def change():
a = 90
print(a)
a = 9
print("Before the function call ", a)
print("inside change function", end=' ')
change()
print("After the function call ", a)
先定义 a,在函数中使用
#!/usr/bin/env python3
a = 9
def change():
print(a)
change()
使用 global 关键字,对函数中的 a 标志为全局变量,让函数内部使用全局变量的 a
#!/usr/bin/env python3
a = 9
def change():
global a
print(a)
a = 100
print("Before the function call ", a)
print("inside change function", end=' ')
change()
print("After the function call ", a)
#!/usr/bin/env python3
def change():
global a
a = 90
print(a)
a = 9
print("Before the function call ", a)
print("inside change function", end=' ')
change()
print("After the function call ", a)
函数的参数变量可以有默认值,也就是说如果我们在调用函数时对指定的参数变量没有给出任何值则会赋其默认值。
>>> def test(a , b=-99):
... if a > b:
... return True
... else:
... return False
具有默认值的参数后面不能再有普通参数,默认值只被赋值一次,因此如果默认值是任何可变对象时会有所不同
>>> def f(a, data=None):
... if data is None:
... data = []
... data.append(a)
... return data
...
>>> print(f(1))
[1]
>>> print(f(2))
[2]
>>> def func(a, b=5, c=10):
... print('a is', a, 'and b is', b, 'and c is', c)
>>> def hello(*, name='User'):
... print("Hello", name)
#!/usr/bin/env python3
import math
def longest_side(a, b):
"""
Function to find the length of the longest side of a right triangle.
:arg a: Side a of the triangle
:arg b: Side b of the triangle
:return: Length of the longest side c as float
"""
return math.sqrt(a*a + b*b)
if __name__ == '__main__':
print(longest_side.__doc__)
print(longest_side(4,5))
高阶函数:使用一个或多个函数作为参数,返回另一个函数作为输出
# 创建一个函数,将参数列表中每个元素都变成全大写
>>> def high(l):
... return [i.upper() for i in l]
...
# 创建高阶函数,接受一个函数和一个列表作为参数
>>> def test(h, l):
... return h(l)
map 它接受一个函数和一个序列(迭代器)作为输入,然后对序列(迭代器)的每一个值应用这个函数,返回一个序列(迭代器),其包含应用函数后的结果。
>>> lst = [1, 2, 3, 4, 5]
>>> def square(num):
... "返回所给数字的平方."
... return num * num
文件处理
使用 open() 函数打开文件
"r",以只读模式打开,你只能读取文件但不能编辑/删除文件的任何内容
"w",以写入模式打开,如果文件存在将会删除里面的所有内容,然后打开这个文件进行写入
"a",以追加模式打开,写入到文件中的任何数据将自动添加到末尾
打开文件后我们应该总是关闭文件,我们使用方法 close() 完成这个操作。
>>> fobj.close()
始终确保你显式关闭每个打开的文件,一旦它的工作完成你没有任何理由保持打开文件。
文件· https://labfile.oss.aliyuncs.com/courses/596/sample.txt
>>> fobj = open("sample.txt")
>>> fobj.read()
'I love Python\nI love shiyanlou\n'
>>> fobj.close()
read(size) 有一个可选的参数 size,用于指定字符串长度。如果没有指定 size 或者指定为负数,就会读取并返回整个文件。
>>> fobj = open("sample.txt")
>>> fobj.readline()
'I love Python\n'
>>> fobj.readline()
'I love shiyanlou\n'
>>> fobj.close()
>>> fobj = open('sample.txt')
>>> fobj.readlines()
['I love Python\n', 'I love shiyanlou\n']
>>> fobj.close()
>>> fobj = open('sample.txt')
>>> for x in fobj:
... print(x, end = '')
...
I love Python
I love shiyanlou
>>> fobj.close()
接受用户输入的字符串作为将要读取的文件的文件名,并且在屏幕上打印文件内容。
#!/usr/bin/env python3
name = input("Enter the file name: ")
fobj = open(name)
print(fobj.read())
fobj.close()
>>> fobj = open("ircnicks.txt", 'w')
>>> fobj.write('powerpork\n')
>>> fobj.write('indrag\n')
>>> fobj.write('mishti\n')
>>> fobj.write('sankarshan')
>>> fobj.close()
>>> fobj = open('ircnicks.txt')
>>> s = fobj.read()
>>> fobj.close()
>>> print(s)
拷贝给定的文本文件到另一个给定的文本文件。
#!/usr/bin/env python3
import sys
if len(sys.argv) < 3:
print("Wrong parameter")
print("./copyfile.py file1 file2")
sys.exit(1)
f1 = open(sys.argv[1])
s = f1.read()
f1.close()
f2 = open(sys.argv[2], 'w')
f2.write(s)
f2.close()
对任意给定文本文件中的制表符、行、空格进行计数。
#!/usr/bin/env python3
import os
import sys
def parse_file(path):
"""
分析给定文本文件,返回其空格、制表符、行的相关信息
:arg path: 要分析的文本文件的路径
:return: 包含空格数、制表符数、行数的元组
"""
fd = open(path)
i = 0
spaces = 0
tabs = 0
for i,line in enumerate(fd):
spaces += line.count(' ')
tabs += line.count('\t')
# 现在关闭打开的文件
fd.close()
# 以元组形式返回结果
return spaces, tabs, i + 1
def main(path):
"""
函数用于打印文件分析结果
:arg path: 要分析的文本文件的路径
:return: 若文件存在则为 True,否则 False
"""
if os.path.exists(path):
spaces, tabs, lines = parse_file(path)
print("Spaces {}. tabs {}. lines {}".format(spaces, tabs, lines))
return True
else:
return False
if __name__ == '__main__':
if len(sys.argv) > 1:
main(sys.argv[1])
else:
sys.exit(-1)
sys.exit(0)
在实际情况中,应该尝试使用 with 语句处理文件对象,它会在文件用完后会自动关闭,就算发生异常也没关系。
>>> with open('sample.txt') as fobj:
... for line in fobj:
... print(line, end = '')
字符串操作
文件 https://labfile.oss.aliyuncs.com/courses/790/String.txt
用 open 打开文件 /tmp/String.txt 并读取其中的字符串
提取字符串中的所有数字,并组合成一个新的字符串,然后打印输出
# 打开并读取文件里的字符串
with open('/tmp/String.txt') as f:
s = f.read()
res = ""
# 循环字符串里的每个字符,判断是否为数字
for char in s:
if char.isdigit():
res += char
print(res)
异常
当访问一个未定义的变量则会发生 NameError
当操作或函数应用于不适当类型的对象时引发TypeError
使用 try...except 块来处理任意异常:
try:
statements to be inside try clause
statement2
statement3
...
except ExceptionName:
statements to evaluated in case of ExceptionName happens
>>> def get_number():
... "Returns a float number"
... number = float(input("Enter a float number: "))
... return number
...
>>>
>>> while True:
... try:
... print(get_number())
... except ValueError:
... print("You entered a wrong value.")
一个空的 except 语句能捕获任何异常
>>> try:
... input() # 输入的时候按下 Ctrl + C 产生 KeyboardInterrupt
... except:
... print("Unknown Exception")
使用 raise 语句抛出一个异常
raise ValueError("A value error happened.")
>>> try:
... raise ValueError("A value error happened.")
... except ValueError:
... print("ValueError in our code.")
try 语句还有另一个可选的 finally 子句,目的在于定义在任何情况下都一定要执行的功能。
>>> try:
... raise KeyboardInterrupt
... finally:
... print('Goodbye, world!')
玩转函数
实现一个程序,将分钟转为小时和分钟。
用户能够通过命令行参数输入分钟数,不要使用 input,命令行参数可以使用 sys.argv 来提取。例如程序执行为 python3 MinutesToHours.py 80,传入的参数 80 就是分钟数,程序需要打印出相应的小时数和分钟数,输出为 1H, 20M。
如果用户输入的是一个负值,程序需要 raise 来抛出 ValueError 异常。
Hours() 函数调用的时候,需要使用 try...except 处理异常。获取异常后,在屏幕上打印出 Parameter Error 提示用户输入的值有误。
import sys
# 转换函数
def Hours(minute):
# 如果为负数则 raise 异常
if minute < 0:
raise ValueError("Input number cannot be negative")
else:
print("{} H, {} M".format(int(minute / 60), minute % 60))
# 函数调用及异常处理逻辑
try:
Hours(int(sys.argv[1]))
except:
print("Parameter Error")
类
在 Python 中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。
以下面这种方式定义类:
class nameoftheclass(parent_class):
statement1
statement2
statement3
_init_ 方法
类的实例化使用函数符号。只要将类对象看作是一个返回新的类实例的无参数函数即可。
x = MyClass()
很多类都倾向于将对象创建为有初始状态的。因此类可能会定义一个名为 init() 的特殊方法,像下面这样:
def __init__(self):
self.data = []
出于弹性的需要,init() 方法可以有参数。事实上,参数通过init() 传递到类的实例化操作上。例如:
>>> class Complex:
... def __init__(self, realpart, imagpart):
... self.r = realpart
... self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
继承
当一个类继承另一个类时,它将继承父类的所有功能(如变量和方法)。这有助于重用代码。
#!/usr/bin/env python3
class Person(object):
"""
返回具有给定名称的 Person 对象
"""
def __init__(self, name):
self.name = name
def get_details(self):
"""
返回包含人名的字符串
"""
return self.name
class Student(Person):
"""
返回 Student 对象,采用 name, branch, year 3 个参数
"""
def __init__(self, name, branch, year):
Person.__init__(self, name)
self.branch = branch
self.year = year
def get_details(self):
"""
返回包含学生具体信息的字符串
"""
return "{} studies {} and is in {} year.".format(self.name, self.branch, self.year)
class Teacher(Person):
"""
返回 Teacher 对象,采用字符串列表作为参数
"""
def __init__(self, name, papers):
Person.__init__(self, name)
self.papers = papers
def get_details(self):
return "{} teaches {}".format(self.name, ','.join(self.papers))
person1 = Person('Sachin')
student1 = Student('Kushal', 'CSE', 2005)
teacher1 = Teacher('Prashad', ['C', 'C++'])
print(person1.get_details())
print(student1.get_details())
print(teacher1.get_details())
多继承
一个类可以继承自多个类,具有父类的所有变量和方法
class MyClass(Parentclass1, Parentclass2,...):
def __init__(self):
Parentclass1.__init__(self)
Parentclass2.__init__(self)
...
...
删除对象
>>> s = "I love you"
>>> del s
del 实际上使对象的引用计数减少一,当对象的引用计数变成零的时候,垃圾回收器会删除这个对象。
属性(attributes)读取方法
>>> class Student(object):
... def __init__(self, name):
... self.name = name
...
>>> std = Student("Kushal Das")
>>> print(std.name)
>>> std.name = "Python"
>>> print(std.name)
装饰器
@property 装饰器就是负责把一个方法变成属性调用的。
#!/usr/bin/env python3
class Account(object):
"""账号类,
amount 是美元金额.
"""
def __init__(self, rate):
self.__amt = 0
self.rate = rate
@property
def amount(self):
"""账号余额(美元)"""
return self.__amt
@property
def cny(self):
"""账号余额(人民币)"""
return self.__amt * self.rate
@amount.setter
def amount(self, value):
if value < 0:
print("Sorry, no negative amount in the account.")
return
self.__amt = value
if __name__ == '__main__':
acc = Account(rate=6.6) # 基于课程编写时的汇率
acc.amount = 20
print("Dollar amount:", acc.amount)
print("In CNY:", acc.cny)
acc.amount = -100
print("Dollar amount:", acc.amount)
模块
但是当人们编写大型程序的时候他们会倾向于将代码分为多个不同的文件以便使用,调试以及拥有更好的可读性。在 Python 中我们使用模块来到达这些目的。模块是包括 Python 定义和声明的文件。文件名就是模块名加上 .py 后缀。
以由全局变量 _name_ 得到模块的模块名(一个字符串)。
创建一个 bars.py 文件
"""
Bars Module
============
这是一个打印不同分割线的示例模块
"""
def starbar(num):
"""打印 * 分割线
:arg num: 线长
"""
print('*' * num)
def hashbar(num):
"""打印 # 分割线
:arg num: 线长
"""
print('#' * num)
def simplebar(num):
"""打印 - 分割线
:arg num: 线长
"""
print('-' * num)
>>> import bars
>>> bars.hashbar(10)
>>> bars.simplebar(10)
>>> bars.starbar(10)
导入模块
>>> from bars import simplebar, starbar
>>> simplebar(20)
包
含有 _init_.py 文件的目录可以用来作为一个包,目录里的所有 .py 文件都是这个包的子模块。
创建 mymodule 目录
cd /home/shiyanlou
mkdir mymodule
将bars.py 拷贝到 mymodule 目录下,然后可以使用 touch 创建一个 utils.py 文件。
使用 touch 命令创建一个空的 _init_.py 文件
touch mymodule/__init__.py
如果 _init_.py 文件内有一个名为 _all_ 的列表,那么只有在列表内列出的名字将会被公开。
from mymodule.bars import simplebar
__all__ = [simplebar, ]
那么导入时将只有 simplebar 可用。
默认模块
使用 help() 函数查找任何模块/类的文档。
>>> help(str)
os 模块
os模块提供了与操作系统相关的功能。
getuid() 函数返回当前进程的有效用户 id。
>>> os.getuid()
getpid() 函数返回当前进程的 id。getppid() 返回父进程的 id。
>>> os.getpid()
>>> os.getppid()
uname() 函数返回识别操作系统的不同信息,在 Linux 中它返回的详细信息可以从 uname -a 命令得到。uname() 返回的对象是一个元组,(sysname, nodename, release, version, machine)。
>>> os.uname()
getcwd() 函数返回当前工作目录。chdir(path) 则是更改当前目录到 path。
>>> os.getcwd()
>>> os.chdir('Code')
>>> os.getcwd()
使用 os 模块提供的另一个函数来创建一个自己的函数,它将列出给定目录下的所有文件和目录。
def view_dir(path='.'):
"""
这个函数打印给定目录中的所有文件和目录
:args path: 指定目录,默认为当前目录
"""
names = os.listdir(path)
names.sort()
for name in names:
print(name, end =' ')
print()
Requests 模块
可以使用 get() 方法获取任意一个网页。
>>> import requests
>>> req = requests.get('https://github.com')
>>> req.status_code
写一个能够从指定的 URL 中下载文件的程序。
#!/usr/bin/env python3
import requests
def download(url):
'''
从指定的 URL 中下载文件并存储到当前目录
url: 要下载页面内容的网址
'''
# 检查 URL 是否存在
try:
req = requests.get(url)
except requests.exceptions.MissingSchema:
print('Invalid URL "{}"'.format(url))
return
# 检查是否成功访问了该网站
if req.status_code == 403:
print('You do not have the authority to access this page.')
return
filename = url.split('/')[-1]
with open(filename, 'w') as fobj:
fobj.write(req.content.decode('utf-8'))
print("Download over.")
if __name__ == '__main__':
url = input('Enter a URL: ')
download(url)
if _name_ == '_main_': 这条语句,它的作用是,只有在当前模块名为 _main_ 的时候(即作为脚本执行的时候)才会执行此 if 块内的语句。换句话说,当此文件以模块的形式导入到其它文件中时,if 块内的语句并不会执行。
argparse 命令行参数处理模块
TAB 补全
先创建一个文件:~/.pythonrc
import rlcompleter, readline
readline.parse_and_bind('tab: complete')
history_file = os.path.expanduser('~/.python_history')
readline.read_history_file(history_file)
import atexit
atexit.register(readline.write_history_file, history_file)
在 ~/.bashrc 文件中设置 PYTHONSTARTUP 环境变量指向这个文件:
export PYTHONSTARTUP=~/.pythonrc
每当你打开 bash shell,你将会有 TAB 补全和 Python 解释器中代码输入的历史记录。
要在当前 shell 中使用,source 这个 bashrc 文件。
source ~/.bashrc
Collections 模块
Counter
Counter 是一个有助于 hashable 对象计数的 dict 子类。它是一个无序的集合,其中 hashable 对象的元素存储为字典的键,它们的计数存储为字典的值,计数可以为任意整数,包括零和负数。
查看 Python 的 LICENSE 文件中某些单词出现的次数。
>>> from collections import Counter
>>> import re
>>> path = '/usr/lib/python3.5/LICENSE.txt'
>>> words = re.findall('\w+', open(path).read().lower())
>>> Counter(words).most_common(10)
Counter 对象有一个叫做 elements() 的方法,其返回的序列中,依照计数重复元素相同次数,元素顺序是无序的。
>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> list(c.elements())
['b','b','a', 'a', 'a', 'a']
most_common() 方法返回最常见的元素及其计数,顺序为最常见到最少。
>>> Counter('abracadabra').most_common(3)
[('a', 5), ('r', 2), ('b', 2)]
defaultdict
defaultdict 是内建 dict 类的子类,它覆写了一个方法并添加了一个可写的实例变量。其余功能与字典相同。
同样的功能使用 defaultdict 比使用 dict.setdefault 方法快。
>>> from collections import defaultdict
>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
... d[k].append(v)
...
>>> d.items()
namedtuple
命名元组有助于对元组每个位置赋予意义,并且让我们的代码有更好的可读性和自文档性。你可以在任何使用元组地方使用命名元组。在例子中我们会创建一个命名元组以展示为元组每个位置保存信息。
>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y']) # 定义命名元组
>>> p = Point(10, y=20) # 创建一个对象
>>> p
Point(x=10, y=20)
>>> p.x + p.y
30
>>> p[0] + p[1] # 像普通元组那样访问元素
30
>>> x, y = p # 元组拆封
>>> x
10
>>> y
20
类和 Collection
改写 我们在 类 这个实验中 继承 部分的 student_teacher.py 脚本,实现以下功能:
在 Person() 类中增添函数 get_grade()
对于教师类,get_grade() 函数可以自动统计出老师班上学生的得分情况并按照频率的高低以 A: X, B: X, C: X, D: X 的形式打印出来
对于学生类,get_grade() 函数则可以以 Pass: X, Fail: X 来统计自己的成绩情况(A,B,C 为 Pass, 如果得了 D 就认为是 Fail)。
#!/usr/bin/env python3
class Person(object):
"""
返回具有给定名称的 Person 对象
"""
def __init__(self, name):
self.name = name
def get_details(self):
"""
返回包含人名的字符串
"""
return self.name
class Student(Person):
"""
返回 Student 对象,采用 name, branch, year 3 个参数
"""
def __init__(self, name, branch, year):
Person.__init__(self, name)
self.branch = branch
self.year = year
def get_details(self):
"""
返回包含学生具体信息的字符串
"""
return "{} studies {} and is in {} year.".format(self.name, self.branch, self.year)
class Teacher(Person):
"""
返回 Teacher 对象,采用字符串列表作为参数
"""
def __init__(self, name, papers):
Person.__init__(self, name)
self.papers = papers
def get_details(self):
return "{} teaches {}".format(self.name, ','.join(self.papers))
person1 = Person('Sachin')
student1 = Student('Kushal', 'CSE', 2005)
teacher1 = Teacher('Prashad', ['C', 'C++'])
print(person1.get_details())
print(student1.get_details())
print(teacher1.get_details())
#!/usr/bin/env python3
import sys
from collections import Counter
class Person(object):
"""
返回具有给定名称的 Person 对象
"""
def __init__(self, name):
self.name = name
def get_details(self):
"""
返回包含人名的字符串
"""
return self.name
def get_grade(self):
return 0
class Student(Person):
"""
返回 Student 对象,采用 name, branch, year 3 个参数
"""
def __init__(self, name, branch, year,grade):
Person.__init__(self, name)
self.branch = branch
self.year = year
self.grade = grade
def get_details(self):
"""
返回包含学生具体信息的字符串
"""
return "{} studies {} and is in {} year.".format(self.name, self.branch, self.year)
def get_grade(self):
common = Counter(self.grade).most_common(4)
n1 = 0
n2 = 0
for item in common:
if item[0] != 'D':
n1 += item[1]
else:
n2 += item[1]
print("Pass: {}, Fail: {}".format(n1,n2))
class Teacher(Person):
"""
返回 Teacher 对象,采用字符串列表作为参数
"""
def __init__(self, name, papers, grade):
Person.__init__(self, name)
self.papers = papers
self.grade = grade
def get_details(self):
return "{} teaches {}".format(self.name, ','.join(self.papers))
def get_grade(self):
s = []
common = Counter(self.grade).most_common(4)
for i,j in common:
s.append("{}: {}".format(i,j))
print(', '.join(s))
person1 = Person('Sachin')
if sys.argv[1] == "student":
student1 = Student('Kushal', 'CSE', 2005, sys.argv[2])
student1.get_grade()
else:
teacher1 = Teacher('Prashad', ['C', 'C++'], sys.argv[2])
teacher1.get_grade()
PEP8 代码风格指南
代码排版
每层缩进使用 4 个空格。
续行要么与圆括号、中括号、花括号这样的被包裹元素保持垂直对齐,要么放在 Python 的隐线(注:应该是相对于 def 的内部块)内部,或者使用悬挂缩进。使用悬挂缩进的注意事项:第一行不能有参数,用进一步的缩进来把其他行区分开。
# Aligned with opening delimiter.
foo = long_function_name(var_one, var_two,
var_three, var_four)
# More indentation included to distinguish this from the rest.
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
# Hanging indents should add a level.
foo = long_function_name(
var_one, var_two,
var_three, var_four)
当 if 语句的条件部分足够长,需要将它写入到多个行,值得注意的是两个连在一起的关键字(i.e. if),添加一个空格,给后续的多行条件添加一个左括号形成自然地 4 空格缩进。如果和嵌套在 if 语句内的缩进代码块产生了视觉冲突,也应该被自然缩进 4 个空格。这份增强建议书对于怎样(或是否)把条件行和 if 语句的缩进块在视觉上区分开来是没有明确规定的。
# No extra indentation.
if (this_is_one_thing and
that_is_another_thing):
do_something()
# Add a comment, which will provide some distinction in editors
# supporting syntax highlighting.
if (this_is_one_thing and
that_is_another_thing):
# Since both conditions are true, we can frobnicate.
do_something()
# Add some extra indentation on the conditional continuation line.
if (this_is_one_thing
and that_is_another_thing):
do_something()
在多行结构中的右圆括号、右中括号、右大括号应该放在最后一行的第一个非空白字符的正下方
my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)
或者放在多行结构的起始行的第一个字符正下方
my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)
空格是首选的缩进方法。
限制每行的最大长度为 79 个字符。
对于那些约束很少的文本结构(文档字符串或注释)的长块,应该限制每行长度为 72 个字符。
限制编辑器窗口宽为 80 来避免自动换行,即使有些编辑工具在换行的时候会在最后一列放一个标识符。
首选的换行方式是在括号(小中大)内隐式换行(非续行符 \)。长行应该在括号表达式的包裹下换行。这比反斜杠作为续行符更好。
with open('/path/to/some/file/you/want/to/read') as file_1, \
open('/path/to/some/file/being/written', 'w') as file_2:
file_2.write(file_1.read())
class Rectangle(Blob):
def __init__(self, width, height,
color='black', emphasis=None, highlight=0):
if (width == 0 and height == 0 and
color == 'red' and emphasis == 'strong' or
highlight > 100):
raise ValueError("sorry, you lose")
if width == 0 and height == 0 and (color == 'red' or
emphasis is None):
raise ValueError("I don't think so -- values are %s, %s" %
(width, height))
Blob.__init__(self, width, height,
color, emphasis, highlight)
顶级函数和类定义上下使用两个空行分隔。
类内的方法定义使用一个空行分隔。
可以使用额外的空行(有节制的)来分隔相关联的函数组。在一系列相关联的单行代码中空行可以省略(e.g. 一组虚拟的实现)。
在函数中使用空白行(有节制的)来表明逻辑部分。
import 不同的模块应该独立一行
import 语句应该总是放在文件的顶部,在模块注释和文档字符串之下,在模块全局变量和常量之前。
import 语句分组顺序如下:
导入标准库模块
导入相关第三方库模块
导入当前应用程序 / 库模块
每组之间应该用空行分开。
然后用 _all_ 声明本文件内的模块。
绝对导入是推荐的,它们通常是更可读的,并且在错误的包系统配置(如一个目录包含一个以 os.path 结尾的包)下有良好的行为倾向(至少有更清晰的错误消息)
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example
相对于绝对导入,相对导入是个可选替代,特别是处理复杂的包结构时,绝对导入会有不必要的冗余:
from . import sibling
from .sibling import example
标准库代码应该避免复杂的包结构,并且永远使用绝对导入。
应该避免通配符导入(from import *),这会使名称空间里存在的名称变得不清晰,迷惑读者和自动化工具。
字符串引号
单引号字符串和双引号字符串是相同的。
一个字符串同时包含单引号和双引号字符时,用另外一种来包裹字符串,而不是使用反斜杠来转义,以提高可读性。
表达式和语句中的空格
避免在下列情况中使用多余的空格:
与括号保持紧凑(小括号、中括号、大括号)
spam(ham[1], {eggs: 2})
与后面的逗号、分号或冒号保持紧凑
if x == 4: print x, y; x, y = y, x
切片内的冒号就像二元操作符一样,任意一侧应该被等同对待(把它当做一个极低优先级的操作)。在一个可扩展的切片中,冒号两侧必须有相同的空格数量。
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]
函数名与其后参数列表的左括号应该保持紧凑
spam(1)
与切片或索引的左括号保持紧凑
dct['key'] = lst[index]
在赋值操作符(或其它)的两侧保持多余一个的空格
x = 1
y = 2
long_variable = 3
总是在这些二元操作符的两侧加入一个空格:赋值(=),增量赋值(+=,-=,...),比较(==,<,>,!=,<>,<=,>=,in,not in,is,is not),布尔运算(and,or,not)。
在不同优先级之间,考虑在更低优先级的操作符两侧插入空格。用你自己的判断力;但不要使用超过一个空格,并且在二元操作符的两侧有相同的空格数。
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
不要在关键值参数或默认值参数的等号两边加入空格。
def complex(real, imag=0.0):
return magic(r=real, i=imag)
带注释的函数定义中的等号两侧要各插入空格。此外,在冒号后用一个单独的空格,也要在表明函数返回值类型的 -> 左右各插入一个空格。
def munge(input: AnyStr):
def munge(sep: AnyStr = None):
def munge() -> AnyStr:
def munge(input: AnyStr, sep: AnyStr = None, limit=1000):
打消使用复合语句(多条语句在同一行)的念头。
if foo == 'blah':
do_blah_thing()
do_one()
do_two()
do_three()
有时候把 if/for/while 和一个小的主体放在同一行也是可行的,千万不要在有多条语句的情况下这样做。此外,还要避免折叠,例如长行。
注释
注释总是随着代码的变更而更新。
注释应该是完整的句子。如果注释是一个短语或语句,第一个单词应该大写,除非是一个开头是小写的标识符(从不改变标识符的大小写)。
块注释通常用来说明跟随在其后的代码,应该与那些代码有相同的缩进层次。块注释每一行以#起头,并且#后要跟一个空格(除非是注释内的缩进文本)。
一个行内注释与语句在同一行。行内注释应该至少与语句相隔两个空格。以#打头,#后接一个空格。
文档字符串:为所有的公共模块、函数、类和方法编写文档字符串。对于非公共的方法,文档字符串是不必要的,但是也应该有注释来说明代码是干什么的。这个注释应该放在方法声明的下面。
版本注记
如果必须要 Subversion,CVS 或 RCS 标记在你的源文件里,像这样做:
__version__ = "$Revision$"
# $Source$
命名约定
API 里对用户可见的公共部分应该遵循约定,反映的是使用而不是实现。
永远不要使用单个字符l(小写字母 el),O(大写字母 oh),或I(大写字母 eye)作为变量名。在一些字体中,这些字符是无法和数字1和0区分开的。试图使用l时用L代替。
模块名应该短,且全小写。如果能改善可读性,可以使用下划线。Python 的包名也应该短,全部小写,但是不推荐使用下划线。
类名通常使用 CapWords 约定。注意和内建名称的区分开:大多数内建名称是一个单独的单词(或两个单词一起),CapWords 约定只被用在异常名和内建常量上。
应该用Error作为你的异常名的后缀(异常实际上是一个错误)。
若被设计的模块可以通过from M import *来使用,它应该使用all机制来表明那些可以可导出的全局变量,或者使用下划线前缀的全局变量表明其是模块私有的。
函数名应该是小写的,有必要的话用下划线来分隔单词提高可读性。
总是使用 self 作为实例方法的第一个参数。总是使用 cls 作为类方法的第一个参数。
用函数名的命名规则:全部小写,用下划线分隔单词提高可读性。用一个且有一个前导的下划线来表明非公有的方法和实例变量。为了避免与子类变量或方法的命名冲突,用两个前导下划线来调用 Python 的命名改编规则。
常量通常是模块级的定义,全部大写,单词之间以下划线分隔。例如MAX_OVERFLOW和TOTAL。
总是决定一个类的方法和变量(属性)是应该公有还是非公有。如果有疑问,选择非公有;相比把共有属性变非公有,非公有属性变公有会容易得多。
公有属性是你期望给那些与你的类无关的客户端使用的,你应该保证不会出现不向后兼容的改变。非公有的属性是你不打算给其它第三方使用的;你不需要保证非公有的属性不会改变甚至被移除也是可以的。
另一种属性的分类是“子类 API”的一部分(通常在其它语言里叫做“Protected”)。一些类被设计成被继承的,要么扩展要么修改类的某方面行为。设计这样一个类的时候,务必做出明确的决定,哪些是公有的,其将会成为子类 API 的一部分,哪些仅仅是用于你的基类的。
公共和内部接口
保证所有公有接口的向后兼容性。
文档化的接口考虑公有,除非文档明确的说明它们是暂时的,或者内部接口不保证其的向后兼容性。所有的非文档化的应该被假设为非公开的。
为了更好的支持内省,模块应该用 _all_ 属性来明确规定公有 API 的名字。设置 _all_ 为空 list 表明模块没有公有 API。
被认为是内部的接口,其包含的任何名称空间(包、模块或类)也被认为是内部的。
迭代器、生成器、装饰器
迭代器
_iter_(),返回迭代器对象自身。这用在 for 和 in 语句中。
_next_(),返回迭代器的下一个值。如果没有下一个值可以返回,那么应该抛出 StopIteration 异常。
class Counter(object):
def __init__(self, low, high):
self.current = low
self.high = high
def __iter__(self):
return self
def __next__(self):
#返回下一个值直到当前值大于 high
if self.current > self.high:
raise StopIteration
else:
self.current += 1
return self.current - 1
>>> c = Counter(5,10)
>>> for i in c:
... print(i, end=' ')
迭代器只能被使用一次。这意味着迭代器一旦抛出 StopIteration,它会持续抛出相同的异常。
>>> c = Counter(5,6)
>>> next(c)
5
>>> next(c)
6
>>> next(c)
>>> iterator = iter(c)
>>> while True:
... try:
... x = iterator.__next__()
... print(x, end=' ')
... except StopIteration as e:
... break
生成器
生成器是更简单的创建迭代器的方法,这通过在函数中使用 yield 关键字完成:
>>> def my_generator():
... print("Inside my generator")
... yield 'a'
... yield 'b'
... yield 'c'
...
>>> my_generator()
我们能在 for 循环中使用它,就像我们使用任何其它迭代器一样。
>>> for char in my_generator():
... print(char)
使用一个生成器函数完成与 Counter 类相同的功能,并且把它用在 for 循环中。
>>> def counter_generator(low, high):
... while low <= high:
... yield low
... low += 1
...
>>> for i in counter_generator(5,10):
... print(i, end=' ')
在 While 循环中,每当执行到 yield 语句时,返回变量 low 的值并且生成器状态转为挂起。在下一次调用生成器时,生成器从之前冻结的地方恢复执行然后变量 low 的值增一。生成器继续 while 循环并且再次来到 yield 语句...
当你调用生成器函数时它返回一个生成器对象。如果你把这个对象传入 dir() 函数,你会在返回的结果中找到 _iter_ 和 _next_ 两个方法名。
通常使用生成器进行惰性求值。这样使用生成器是处理大数据的好方法。如果你不想在内存中加载所有数据,你可以使用生成器,一次只传递给你一部分数据。
os.path.walk() 函数是最典型的这样的例子,它使用一个回调函数和当前的 os.walk 生成器。使用生成器实现节约内存。
可以使用生成器产生无限多的值。
>>> def infinite_generator(start=0):
... while True:
... yield start
... start += 1
...
>>> for num in infinite_generator(4):
... print(num, end=' ')
... if num > 20:
... break
如果我们回到 my_generator() 这个例子,我们会发现生成器的一个特点:它们是不可重复使用的。
>>> g = my_generator()
>>> for c in g:
... print(c)
生成器表达式
生成器表达式是列表推导式和生成器的一个高性能,内存使用效率高的推广。
尝试对 1 到 9 的所有数字进行平方求和。
>>> sum([x*x for x in range(1,10)])
这个例子实际上首先在内存中创建了一个平方数值的列表,然后遍历这个列表,最终求和后释放内存。
>>> sum(x*x for x in range(1,10))
生成器表达式的语法要求其总是直接在在一对括号内,并且不能在两边有逗号。
>>> sum(x*x for x in range(1,10))
>>> g = (x*x for x in range(1,10))
>>> g
>>> jobtext = 'anacron'
>>> all = (line for line in open('/etc/crontab', 'r') )
>>> job = ( line for line in all if line.find(jobtext) != -1)
>>> text = next(job)
>>> text
'25 6\t* * *\troot\ttest -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )\n'
>>> text = next(job)
>>> text
'47 6\t* * 7\troot\ttest -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )\n'
>>> text = next(job)
>>> text
'52 6\t1 * *\troot\ttest -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )\n'
闭包
由另外一个函数返回的函数。我们使用闭包去除重复代码。在下面的例子中我们创建了一个简单的闭包来对数字求和。
>>> def add_number(num):
... def adder(number):
... #adder 是一个闭包
... return num + number
... return adder
>>> a_10 = add_number(10)
>>> a_10(21)
>>> a_10(34)
>>> a_5 = add_number(5)
>>> a_5(3)
装饰器
用来给一些对象动态的添加一些新的行为,我们使用过的闭包也是这样的。
>>> def my_decorator(func):
... def wrapper(*args, **kwargs):
... print("Before call")
... result = func(*args, **kwargs)
... print("After call")
... return result
... return wrapper
...
>>> @my_decorator
... def add(a, b):
... #我们的求和函数
... return a + b
...
>>> add(1, 3)
Virtualenv
$ sudo apt-get update
$ sudo apt-get install python3-pip
$ sudo pip3 install virtualenv
$ cd /home/shiyanlou
$ mkdir virtual
$ cd virtual
$ virtualenv virt1
$ source virt1/bin/activate
(virt1)shiyanlou:~/$
关闭虚拟环境
(virt1)$ deactivate
测试
测试范围
如果可能的话,代码库中的所有代码都要测试。有一个坚实可靠的测试套件,你可以做出大的改动,并确信外部可见行为保持不变。
单元测试
又称为模块测试, 是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。
阶乘计算程序
import sys
def fact(n):
"""
阶乘函数
:arg n: 数字
:returns: n 的阶乘
"""
if n == 0:
return 1
return n * fact(n -1)
def div(n):
"""
只是做除法
"""
res = 10 / n
return res
def main(n):
res = fact(n)
print(res)
if __name__ == '__main__':
if len(sys.argv) > 1:
main(int(sys.argv[1]))
fact(n) 这个函数执行所有的计算,所以我们至少应该测试这个函数。
import unittest
from factorial import fact
class TestFactorial(unittest.TestCase):
"""
我们的基本测试类
"""
def test_fact(self):
"""
实际测试
任何以 `test_` 开头的方法都被视作测试用例
"""
res = fact(5)
self.assertEqual(res, 120)
if __name__ == '__main__':
unittest.main()
各类 assert 语句
异常测试
如果我们在 factorial.py 中调用 div(0),我们能看到异常被抛出。
import unittest
from factorial import fact, div
class TestFactorial(unittest.TestCase):
"""
我们的基本测试类
"""
def test_fact(self):
"""
实际测试
任何以 `test_` 开头的方法都被视作测试用例
"""
res = fact(5)
self.assertEqual(res, 120)
def test_error(self):
"""
测试由运行时错误引发的异常
"""
self.assertRaises(ZeroDivisionError, div, 0)
if __name__ == '__main__':
unittest.main()
mounttab.py
mounttab.py 中只有一个 mount_details() 函数,函数分析并打印挂载详细信息。
import os
def mount_details():
"""
打印挂载详细信息
"""
if os.path.exists('/proc/mounts'):
fd = open('/proc/mounts')
for line in fd:
line = line.strip()
words = line.split()
print('{} on {} type {}'.format(words[0],words[1],words[2]), end=' ')
if len(words) > 5:
print('({})'.format(' '.join(words[3:-2])))
else:
print()
fd.close()
if __name__ == '__main__':
mount_details()
现在我们在 mounttab2.py 中重构了上面的代码并且有一个我们能容易的测试的新函数 parse_mounts()。
import os
def parse_mounts():
"""
分析 /proc/mounts 并 返回元祖的列表
"""
result = []
if os.path.exists('/proc/mounts'):
fd = open('/proc/mounts')
for line in fd:
line = line.strip()
words = line.split()
if len(words) > 5:
res = (words[0],words[1],words[2],'({})'.format(' '.join(words[3:-2])))
else:
res = (words[0],words[1],words[2])
result.append(res)
fd.close()
return result
def mount_details():
"""
打印挂载详细信息
"""
result = parse_mounts()
for line in result:
if len(line) == 4:
print('{} on {} type {} {}'.format(*line))
else:
print('{} on {} type {}'.format(*line))
if __name__ == '__main__':
mount_details()
测试代码,编写 mounttest.py 文件:
#!/usr/bin/env python
import unittest
from mounttab2 import parse_mounts
class TestMount(unittest.TestCase):
"""
我们的基本测试类
"""
def test_parsemount(self):
"""
实际测试
任何以 `test_` 开头的方法都被视作测试用例
"""
result = parse_mounts()
self.assertIsInstance(result, list)
self.assertIsInstance(result[0], tuple)
def test_rootext4(self):
"""
测试找出根文件系统
"""
result = parse_mounts()
for line in result:
if line[1] == '/' and line[2] != 'rootfs':
self.assertEqual(line[2], 'ext4')
if __name__ == '__main__':
unittest.main()
测试覆盖率
测试覆盖率是找到代码库未经测试的部分的简单方法。它并不会告诉你的测试好不好。
$ sudo pip3 install coverage
$ coverage3 run mounttest.py
我们还可以使用下面的命令以 HTML 文件的形式输出覆盖率结果,然后在浏览器中查看它。
$ coverage3 html
项目结构
创建 Python 项目
实验项目名为 factorial,放到 /home/shiyanlou/factorial 目录:
$ cd /home/shiyanlou
$ mkdir factorial
$ cd factorial/
将要创建的 Python 模块取名为 myfact,因此我们下一步创建 myfact 目录。
$ mkdir myfact
$ cd myfact/
主代码将在 fact.py 文件里面。
"myfact module"
def factorial(num):
"""
返回给定数字的阶乘值
:arg num: 我们将计算其阶乘的整数值
:return: 阶乘值,若传递的参数为负数,则为 -1
"""
if num >= 0:
if num == 0:
return 1
return num * factorial(num -1)
else:
return -1
还有模块的 _init_.py 文件,内容如下:
from fact import factorial
__all__ = [factorial, ]
在 factorial 目录下添加了一个 README.rst 文件。
MANIFEST.in
写一个 /home/shiyanlou/factorial/MANIFEST.in 文件,它用来在使用 sdist 命令的时候找出将成为项目源代码压缩包一部分的所有文件。
include *.py
include README.rst
如果你想要排除某些文件,你可以在这个文件中使用 exclude 语句。
安装 python-setuptools 包
$ sudo pip3 install setuptools
setup.py
写一个 /home/shiyanlou/factorial/setup.py,用来创建源代码压缩包或安装软件。
#!/usr/bin/env python3
"""Factorial project"""
from setuptools import find_packages, setup
setup(name = 'factorial', # 注意这里的name不要使用factorial相关的名字,因为会重复,需要另外取一个不会与其他人重复的名字
version = '0.1',
description = "Factorial module.",
long_description = "A test module for our book.",
platforms = ["Linux"],
author="ShiYanLou",
author_email="[email protected]",
url="/courses/596",
license = "MIT",
packages=find_packages()
)
name 是项目名称,version 是发布版本,description 和 long_description_ 分别是项目介绍,项目长描述。platforms 是此模块的支持平台列表。_find_packages() 是一个能在你源目录下找到所有模块的特殊函数
创建一个源文件发布版本,执行以下命令。
python3 setup.py sdist
能在 dist 目录下看到一个 tar 压缩包。
ls dist/
执行下面的命令从源代码安装。
$ sudo python3 setup.py install
Python Package Index (PyPI)
PyPI 的测试服务器 https://testpypi.python.org/pypi。
在这个链接注册账号。你会收到带有链接的邮件,点击这个链接确认你的注册。
创建 ~/.pypirc 文件,存放你的账号详细信息,其内容格式如下:
[distutils]
index-servers = pypi
testpypi
[pypi]
repository: https://upload.pypi.org/legacy/
username:
password:
[testpypi]
repository:https://test.pypi.org/legacy/
username:
password:
替换
记得在 setup.py 中更改项目的名称为其它的名字来测试下面的指令,在接下来的命令中我将项目名称修改为 factorial2,为了不重复,大家需要自行修改至其它名称(不要使用 factorial 和 factorial2,因为已经被使用了)。
将我们的项目到 TestPyPI 服务。这通过 twine 命令完成。
使用 -r 把它指向测试服务器。
$ sudo pip3 install twine
$ twine upload dist/* -r testpypi
也可以使用下面的命令上传到 PyPI 服务上,但这里需要注意,在 ~/.pypirc 里面,你需要到 https://pypi.python.org页面,按照上面的步骤去注册一个账号,然后到~/.pypirc 的 [pypi] 下填写相应的用户名和密码。testpypi 和 pypi 的账号密码并不通用。
$ twine upload dist/* -r pypi
Flask 介绍
Flask 是一个 web 框架。也就是说 Flask 为你提供工具,库和技术来允许你构建一个 web 应用程序。这个 web 应用程序可以是一些 web 页面、博客、wiki、基于 web 的日历应用或商业网站。
Flask 属于微框架(micro-framework)这一类别,微架构通常是很小的不依赖于外部库的框架。这既有优点也有缺点,优点是框架很轻量,更新时依赖少,并且专注安全方面的 bug,缺点是,你不得不自己做更多的工作,或通过添加插件增加自己的依赖列表。
Web 服务器网关接口(Python Web Server Gateway Interface,缩写为 WSGI)是为Python语言定义的Web 服务器和Web 应用程序或框架之间的一种简单而通用的接口。自从 WSGI 被开发出来以后,许多其它语言中也出现了类似接口。
模板引擎:使用模板你可以设置你的页面的基本布局,并提及哪个元素将发生变化。这种方式可以定义您的网页头部并在您的网站的所有页面使它保持一致,如果你需要改变网页头部,你只需要更新一个地方。
"Hello World" 应用
$ sudo pip3 install flask
$ cd /home/shiyanlou
$ mkdir -p hello_flask/{templates,static}
templates 文件夹是存放模板的地方,static 文件夹存放 web 应用所需的静态文件(images, css, javascript)。
$ cd hello_flask
$ vim hello_flask.py
hello_flask.py 文件里编写如下代码:
#!/usr/bin/env python3
import flask
# Create the application.
APP = flask.Flask(__name__)
@APP.route('/')
def index():
""" 显示可在 '/' 访问的 index 页面
"""
return flask.render_template('index.html')
if __name__ == '__main__':
APP.debug=True
APP.run()
创建模板文件 index.html
$ vim templates/index.html
index.html 文件内容如下:
Hello world!
It works!
运行 flask 应用程序
$ python3 hello_flask.py
Flask 中使用参数
更新 hello_flask.py 文件。
在 hello_flask.py 文件中添加以下条目
@APP.route('/hello//')
def hello(name):
""" Displays the page greats who ever comes to visit it.
"""
return flask.render_template('hello.html', name=name)
创建下面这个模板 hello.html
Hello
Hello {{name}}
运行 flask 应用
$ python3 hello_flask.py
额外工作
对于每一个页面我们都创建了一个模板,其实这是不好的做法,我们应该做的是创建一个主模板并且在每个页面使用它。
创建模板文件 master.html。
{% block title %}{% endblock %} - Hello Flask!
{% block body %}{% endblock %}
调整模板 index.html。
在模板 index.html 中,我们声明这个模板扩展自 master.html 模板,然后我们定义了内容来放在这两个部分中(blocks)。在第一个 block title 中,我们放置了 Home 单词,在第二个 block body 中我们定义了我们想要在页面的 body 中有的东西。