Python杂记

[TOC]

小技巧

format()按参数位宽输出

1
print("{:>{<width>}d}".format(n, width))

错误点

1、变量作用域问题

每当标识符被分配一个值,这个赋值有其作用范围(作用域)。通常函数体内的赋值是在函数体内,对函数外的标识符无影响。

可使用global x先定义全局变量。

每一个作用域有一个名称,叫做命名空间(namespace)

如何实现?

通过字典将标识符映射到相应字典的键(key)下。

1
2
3
4
5
6
7
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'f']

>>> del(a)

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'f']

如何搜索?

命令中有指定标识符后,首先在本地命名空间搜索,然后向外层搜索。

P.S.每个对象和类都有自己的namespace。

2、下标意义问题

麻烦出在:①从0开始;②[n:m]只会到m-1

多处使用同一个下标,只要记住下标i代表的实际意义;

分段访问,先考虑边界下标,再+1

使用通式思考问题。

3、情况考虑不周

先考虑平凡情况,后考虑一般情况。

检查范围+分段

4、算法问题

(T_T)

┏┛墓┗┓...(((m -__-)m

━┳━ ━┳━

round()的坑

1
2
3
4
>>> round(2.456,2) # 浮点数四舍五入
2.46
>>> round(2,753) # 默认保留整数
2

1、版本

1
2
3
4
>>> round(0.5) # Python3
0
>>> round(0.5) # Python2
1

python2.7:"Values are rounded to the closest multiple of 10 to the power minus ndigits; if two multiples are equally close, rounding is done away from 0."

如果距离两端一样远,则保留到离0远的一边。比如round(0.5)会近似到1,而round(-0.5)会近似到-1。

python3.5:"values are rounded to the closest multiple of 10 to the power minus ndigits; if two multiples are equally close, rounding is done toward the even choice."

如果距离两边一样远,会保留到偶数的一边。比如round(0.5)和round(-0.5)会保留到0,而round(1.5)会保留到2。

2、浮点数精度

1
2
>>> round(2.675, 2) # python2或python3中均应为2.68
2.67

二进制无法准确表示所有十进制小数。

3、解决办法

1、避免使用round()四舍五入。

2、使用math.ceiling天花板除法。

3、使用//div()得到整除及余数。

4、使用decimal模块。

input()模拟读取到EOF

C语言中可以通过while(scanf() != EOF)来实现遇到文件结尾或者stdin中输入结束后,退出循环读取的效果。

但是Python中没有与EOF相同功能的值,只有一个EOFError

所以我们可以采用try except的方式来实现:

1
2
3
4
5
6
7
while True:
try:
temp = input()
print(temp)
except Exception as e:
print(e)
break

位运算实现四则运算

值得注意的是,python中支持大整数,因此不会产生溢出,补码也就没有了的概念。

以下代码实现了在32 bits机器上的整数计算,模拟了溢出。

但是没必要嘛。。。。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#!/usr/bin/python
# -*- coding: utf-8 -*-

class BinCalc:
def add(self, num1, num2):
# 32bits integer max/min
MAX = 0x7FFFFFFF
MASK = 0xFFFFFFFF

ans = num1
while num2 != 0:
ans = (num1 ^ num2) & MASK
num2 = ((num1 & num2) << 1) & MASK
num1 = ans
return ans if ans <= MAX else ~(ans ^ MASK)

def subtract(self, num1, num2):
mid = self.add(~num2, 1)
return self.add(num1, mid)

def is_negative(self, num1, num2):
return (num1 ^ num2) < 0

def abs(self, num):
if num >= 0:
return num
else:
return self.add(~num, 1)

def multiply(self, num1, num2):
abs1 = self.abs(num1)
abs2 = self.abs(num2)
ans = 0
while abs2 != 0:
if abs2 & 1:
ans = self.add(ans, abs1)
abs2 = abs2 >> 1
abs1 = abs1 << 1
if self.is_negative(num1, num2):
return self.add(~ans, 1)
return ans



def divide(self, num1, num2):
# exception
if num2 == 0:
raise Exception("Divisor is zero.", num2)

abs1 = self.abs(num1)
abs2 = self.abs(num2)

ans = 0
i = 31
while i >= 0:
if (abs1 >> i) >= abs2:
ans = self.add(ans, 1 << i)
abs1 = self.subtract(abs1, abs2 << i)
i = self.subtract(i, 1)
if self.is_negative(num1, num2):
return self.add(~ans, 1)
return ans

列表的空白初始化

这是一个 copydeep copy 问题。

1
2
# 解析语法初始化
lis = [[0 for i in range(n)] for j in range(m)]
1
2
3
# 浅复制初始化
# 子列表指向同一个地址
lis = [[0] * i] * j

字符串判断

1
2
3
4
5
6
7
8
9
10
11
12
13
str.isalnum() # 数字或者字母,为真返回 Ture,否则返回 False。

str.isalpha() # 字母,为真返回 Ture,否则返回 False。

str.isdigit() # 数字,为真返回 Ture,否则返回 False。

str.islower() # 小写,为真返回 Ture,否则返回 False。

str.isupper() # 大写,为真返回 Ture,否则返回 False。

str.istitle() # 首字母大写,为真返回 Ture,否则返回 False。

str.isspace() # 空白字符,为真返回 Ture,否则返回 False。

bool()运用

1
2
3
4
5
6
bool()
# 默认返回False
bool(foo)
# 可以为值foo创造一个布尔类型,多用于控制结构的条件
# 数字:为0则为False
# 序列:为空则为False

判断奇偶数

与1做与(&)运算,若为偶数,则二进制数末尾为0,&1后为0;反之,为1。

1
2
k & 1 == 0 # 偶数
k & 1 == 1 # 奇数

解析语法

1
2
3
[expression for value in iterable if condition]
[pow(i, 2) for i in range(1, n + 1) if i & 1 == 0]
# 计算1-n全部偶数的平方和

yield生成器函数

yield在函数中就如同print()或者list.append()一样,只不过作为生成器有懒惰计算的特性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def factors(n):
'''使用列表的普通方法'''
'''返回n的全部因子在列表中'''
results = [n]
for i in range(1,int(n/2)+1):
if n % i == 0:
results.append(i)
return results

def factorsX(n):
'''yield返回n的全部因子'''
for i in range(1, n+1):
if n % i == 0:
yield i
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def factorsXX(n):
'''yield返回n的全部因子(计算减半,因子无序)'''
k = 1
while k * k < n:
if n % k == 0:
yield k
yield n // k
k += 1
if k * k == n:
yield k

def factorsS(n):
'''yield返回n的全部因子(计算减半,因子有序)'''
k = 1
temp = []
while k * k < n:
if n % k == 0:
yield k
temp.append(n // k)
k += 1
if k * k == n:
yield k
for i in temp[::-1]:
yield i

查找列表中相同元素出现的位置

1、破坏列表法

1
2
3
4
5
6
7
8
9
10
11
12
def function_s(l, x, n):
# 获取第一个x的下标
index_one = l.index(x)
# 删除第一个出现的"a"元素
l.pop(index_one)
i = 1
for i in range(n-1):
index_temp = l.index(x)
i += index_temp
l.pop(index_temp)
return i
# 这种方法必须保证列表内至少有n个元素

2、for循环法

1
2
3
4
5
6
7
8
9
10
11
def function_s(l, x):
# 定义变量, 记录x出现次数
m = 0
# 定义变量, 记录循环到的列表下标
n = 0
for i in l:
if i == x:
print(i)
n += 1
m += 1
return m

3、index()法

1
2
3
4
5
6
# 定义列表
l = ["a", "b", "c", "a", "b", "c", "a"]
# 获取第一个"a"的下标
index_one = l.index("a")
# 从第一个"a"的下一个位置开始查找, 加1
print(l.index("a", index_one + 1))

4、enumerate()函数枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 定义列表
l = ["a", "b", "c", "a", "b", "c", "a"]

# 定义通用的获取某元素在列表中第n次出现的位置下标的函数
def get_index(l, x, n):
# 函数作用: 获取某个元素第n次出现在列表的下标
# 参数列表: 第一个参数为可迭代对象, 第二个参数为要查找的数, 第三个参数为要查找第几个出现的x
l_count = l.count(x)
result = None
if n <= l_count:
num = 0
# enumerate is useful for obtaining an indexed list:
# (0, seq[0]), (1, seq[1]), (2, seq[2]), ...
for item in enumerate(l):
if item[1] == x:
num += 1
if num == n:
result = item[0]
break
else:
print("列表里总共有{}个{}".format(l_count, x))
return result

# 调用函数, 获取结果
result_l = get_index(l, "a", 2)
print(result_l)

嵌套列表展开

1、遍历法

1
2
3
4
5
list_0 = [[1, 2], [3], [4, 5, 6]]
list_1 = []
for lis in list_0:
list_1 += lis
print(list_1)

2、列表推导式

1
2
3
list_0 = [[1, 2], [3, 4, 5]]
list_1 = [i for k in list_1 for i in k]
print(list_1)

3、sum函数

1
2
3
list_0 = [[1, 2], [3], [4, 5, 6]]
list_2 = sum(list_0, [])
print(list_2)

4、递归法

1
2
3
4
5
6
7
8
9
10
def flat(iters):
res = []
for item in iters:
# 如果是列表或者元组,则递归调用
if isinstance(item, list) or isinstance(item, tuple):
res.extend(flat(item))
# 如果是其他类型,则直接加入列表
else:
res.append(item)
return res

素数算法

欧拉筛法

1
2
3
4
5
6
7
8
9
10
11
12
13
def OlaPrimeCalc(n):
Prime = []
Compare = [1] * (n + 1)
for x in range(2, n + 1):
if Compare[x] == 1:
Prime.append(x)
for p in Prime:
if x * p > n:
break
Compare[x * p] = 0
if x % p == 0:
break
return Prime

埃氏筛法

1
2
3
4
5
6
7
8
9
def eladuosai(n):
l = list(range(1,n+1))
l[0] = 0
for i in range(2,n+1):
if l[i-1] != 0 :
for j in range(i*2,n+1,i):
l[j-1] = 0
result = [x for x in l if x != 0]
return set(result)

正整数拆分算法

简洁递归法

分解的正整数超出100,时间即大幅增加。

1
2
3
4
5
6
7
8
9
10
def f(n,a):
'''从a开始拆分n,如果a>(n/2),则出现重复情况'''
if(a>n):
return 0
m=n/2
s=1
while(a<=m):
s+=f(n-a,a)
a+=1
return s

输出情况法

分解的正整数超出80,时间即大幅增加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import sys
sys.setrecursionlimit(5000000) # set the maximum depth as 5000000
global Answer
times = 0
Answer = [0] * 10000
def Calc(x,k):
'''从1开始分解x,第一位储存在第k位,将分解的情况放入列表'''
for num in range(1, x+1):
if num >= Answer[k-1]:
Answer[k] = num
rest = x - num
if rest == 0:
global times
times += 1
else:
Calc(rest, k + 1)

母函数法

1

五边形数定理法

1

文本读取的编码问题

使用 utf-8 读取 Windows 的 .txt 文本文件,读取的第一个字符串开头出现 \ufeff

例如:

1
2
待读取的文本内容
文本第二行
1
2
3
4
5
with open(file_path, 'r', encoding = "utf-8") as fo:
text_list = fo.readlines()

>>> text_list
>>> ['\ufeff待读取的文本内容', '文本第二行']

原因:Windows 保存时采用了 UTF-8 with BOM 编码,并不是标准的 UTF-8。因此文本保存时包含了BOM(Byte Order Mark,字节顺序标记,出现在文本文件头部,Unicode 编码标准中用于标识文件是采用哪种格式的编码)

解决方法:使用 utf-8-sig 编码方式。