1. Запутанная операция
В этом разделе сравниваются некоторые запутанные операции Python.
1.1 Случайная выборка с заменой и случайная выборка без замены
import random
random.choices(seq, k=1) # 长度为k的list,有放回采样
random.sample(seq, k) # 长度为k的list,无放回采样
1.2 Параметры лямбда-функций
func = lambda y: x + y # x的值在函数运行时被绑定
func = lambda y, x=x: x + y # x的值在函数定义时被绑定
1.3 копирование и глубокое копирование
import copy
y = copy.copy(x) # 只复制最顶层
y = copy.deepcopy(x) # 复制所有嵌套部分
Легко запутаться, когда копирование и псевдоним переменных объединяются:
a = [1, 2, [3, 4]]
# Alias.
b_alias = a
assert b_alias == a and b_alias is a
# Shallow copy.
b_shallow_copy = a[:]
assert b_shallow_copy == a and b_shallow_copy is not a and b_shallow_copy[2] is a[2]
# Deep copy.
import copy
b_deep_copy = copy.deepcopy(a)
assert b_deep_copy == a and b_deep_copy is not a and b_deep_copy[2] is not a[2]
Изменение псевдонима повлияет на исходную переменную.Элемент в (поверхностной) копии является псевдонимом элемента в исходном списке, в то время как глубокая копия копируется рекурсивно, и изменение глубокой копии не влияет на оригинал Переменная.
1.4 == и есть
x == y # 两引用对象是否有相同值
x is y # 两引用是否指向同一对象
1.5 Тип решения
type(a) == int # 忽略面向对象设计中的多态特征
isinstance(a, int) # 考虑了面向对象设计中的多态特征
1.6 Поиск строки
str.find(sub, start=None, end=None); str.rfind(...) # 如果找不到返回-1
str.index(sub, start=None, end=None); str.rindex(...) # 如果找不到抛出ValueError异常
1.7 Обратная индексация списка
Это просто вопрос привычки: при прямом индексировании нижний индекс начинается с 0. Если обратный индекс также хочет начинаться с 0, вы можете использовать ~.
print(a[-1], a[-2], a[-3])
print(a[~0], a[~1], a[~2])
2. Руководство пользователя C/C++
Многие пользователи Python в прошлом мигрировали с C/C++.Эти два языка имеют некоторые различия в синтаксисе, стиле кода и т. д. В этом разделе они кратко представлены.
2.1 Большие и маленькие числа
Привычка C/C++ состоит в том, чтобы определять большое число, а в Python есть inf и -inf:
a = float('inf')
b = float('-inf')
2.2 Логические значения
Привычка C/C++ состоит в том, чтобы использовать 0 и ненулевые значения для представления True и False, а Python рекомендует использовать True и False для непосредственного представления логических значений.
a = True
b = False
2.3 Оценка пустоты
Привычка C/C++ оценивать нулевые указатели - это if (a) и if (!a). Решение Python для None:
if x is None:
pass
Если вы используете if not x, другие объекты (такие как строки нулевой длины, списки, кортежи, словари и т. д.) будут рассматриваться как False.
2.4 Поменять местами значения
Привычка C/C++ состоит в том, чтобы определять временную переменную для обмена значениями. Используя операцию Python Tuple, это можно сделать за один шаг.
a, b = b, a
2.5 Сравнение
Соглашение C/C++ заключается в использовании двух условий. Вы можете сделать все это за один шаг с помощью Python.
if 0 < a < 5:
pass
2.6 Установка и получение членов класса
Привычка C/C++ состоит в том, чтобы делать члены класса закрытыми и получать доступ к их значениям через ряд функций Set и Get. Хотя в Python также можно задать соответствующие функции Set и Get через @property, @setter, @deleter, следует избегать лишних абстракций, которые будут в 4-5 раз медленнее, чем прямой доступ.
2.7 Входные и выходные параметры функций
Привычка C/C++ состоит в том, чтобы перечислять входные и выходные параметры как параметры функции, изменять значение выходного параметра через указатель, возвращаемое значение функции является состоянием выполнения, а вызывающая функция проверяет возвращаемое значение. значение, чтобы определить, успешно ли выполнено выполнение. В Python вызывающему функцию не нужно проверять возвращаемое значение, и исключение выдается сразу, когда в функции встречается особый случай.
2.8 Чтение файлов
По сравнению с C/C++, Python намного проще для чтения файлов: открытый файл представляет собой итерируемый объект, который возвращает по одной строке за раз.
with open(file_path, 'rt', encoding='utf-8') as f:
for line in f:
print(line) # 末尾的\n会保留
2.9 Объединение путей к файлам
Привычка C / C ++ обычно использует + для прямого объединения путей, что очень подвержено ошибкам. os.path.join в Python автоматически дополняет разделитель / или \ между путями в зависимости от разных операционных систем:
import os
os.path.join('usr', 'lib', 'local')
2.10 Анализ параметров командной строки
Хотя Python также может использовать sys.argv для разбора параметров командной строки напрямую, как C/C++, более удобно и мощнее использовать инструмент ArgumentParser в argparse.
2.11 Вызов внешних команд
Хотя Python также может использовать os.system для прямого вызова внешних команд, таких как C/C++, но использование subprocess.check_output может свободно выбирать, выполнять ли Shell или нет, а также может получать результаты выполнения внешних команд.
import subprocess
# 如果外部命令返回值非0,则抛出subprocess.CalledProcessError异常
result = subprocess.check_output(['cmd', 'arg1', 'arg2']).decode('utf-8')
# 同时收集标准输出和标准错误
result = subprocess.check_output(['cmd', 'arg1', 'arg2'], stderr=subprocess.STDOUT).decode('utf-8')
# 执行shell命令(管道、重定向等),可以使用shlex.quote()将参数双引号引起来
result = subprocess.check_output('grep python | wc > out', shell=True).decode('utf-8')
2.12 Не изобретайте велосипед
Не изобретайте велосипед, Python называет батареи включенными, а это означает, что Python предлагает решения для многих распространенных проблем.
3. Общие инструменты
3.1 Чтение и запись файлов CSV
import csv
# 无header的读写
with open(name, 'rt', encoding='utf-8', newline='') as f: # newline=''让Python不将换行统一处理
for row in csv.reader(f):
print(row[0], row[1]) # CSV读到的数据都是str类型
with open(name, mode='wt') as f:
f_csv = csv.writer(f)
f_csv.writerow(['symbol', 'change'])
# 有header的读写
with open(name, mode='rt', newline='') as f:
for row in csv.DictReader(f):
print(row['symbol'], row['change'])
with open(name, mode='wt') as f:
header = ['symbol', 'change']
f_csv = csv.DictWriter(f, header)
f_csv.writeheader()
f_csv.writerow({'symbol': xx, 'change': xx})
Обратите внимание, что при слишком большом размере CSV-файла будет сообщено об ошибке: _csv.Error: поле больше предела поля (131072), что можно устранить, изменив верхний предел.
import sys
csv.field_size_limit(sys.maxsize)
csv также может читать данные, разделенные \t
f = csv.reader(f, delimiter='\t')
3.2 Инструменты итератора
Многие инструменты итераторов определены в itertools, например инструмент подпоследовательности:
import itertools
itertools.islice(iterable, start=None, stop, step=None)
# islice('ABCDEF', 2, None) -> C, D, E, F
itertools.filterfalse(predicate, iterable) # 过滤掉predicate为False的元素
# filterfalse(lambda x: x < 5, [1, 4, 6, 4, 1]) -> 6
itertools.takewhile(predicate, iterable) # 当predicate为False时停止迭代
# takewhile(lambda x: x < 5, [1, 4, 6, 4, 1]) -> 1, 4
itertools.dropwhile(predicate, iterable) # 当predicate为False时开始迭代
# dropwhile(lambda x: x < 5, [1, 4, 6, 4, 1]) -> 6, 4, 1
itertools.compress(iterable, selectors) # 根据selectors每个元素是True或False进行选择
# compress('ABCDEF', [1, 0, 1, 0, 1, 1]) -> A, C, E, F
Сортировка последовательности:
sorted(iterable, key=None, reverse=False)
itertools.groupby(iterable, key=None) # 按值分组,iterable需要先被排序
# groupby(sorted([1, 4, 6, 4, 1])) -> (1, iter1), (4, iter4), (6, iter6)
itertools.permutations(iterable, r=None) # 排列,返回值是Tuple
# permutations('ABCD', 2) -> AB, AC, AD, BA, BC, BD, CA, CB, CD, DA, DB, DC
itertools.combinations(iterable, r=None) # 组合,返回值是Tuple
itertools.combinations_with_replacement(...)
# combinations('ABCD', 2) -> AB, AC, AD, BC, BD, CD
Объединить несколько последовательностей:
itertools.chain(*iterables) # 多个序列直接拼接
# chain('ABC', 'DEF') -> A, B, C, D, E, F
import heapq
heapq.merge(*iterables, key=None, reverse=False) # 多个序列按顺序拼接
# merge('ABF', 'CDE') -> A, B, C, D, E, F
zip(*iterables) # 当最短的序列耗尽时停止,结果只能被消耗一次
itertools.zip_longest(*iterables, fillvalue=None) # 当最长的序列耗尽时停止,结果只能被消耗一次
3.3 Счетчик
Счетчик подсчитывает количество вхождений каждого элемента в итерируемом объекте.
import collections
# 创建
collections.Counter(iterable)
# 频次
collections.Counter[key] # key出现频次
# 返回n个出现频次最高的元素和其对应出现频次,如果n为None,返回所有元素
collections.Counter.most_common(n=None)
# 插入/更新
collections.Counter.update(iterable)
counter1 + counter2; counter1 - counter2 # counter加减
# 检查两个字符串的组成元素是否相同
collections.Counter(list1) == collections.Counter(list2)
3.4 Dict со значениями по умолчанию
При доступе к несуществующему ключу defaultdict установит для него некоторое значение по умолчанию.
import collections
collections.defaultdict(type) # 当第一次访问dict[key]时,会无参数调用type,给dict[key]提供一个初始值
3.5 Упорядоченный словарь
import collections
collections.OrderedDict(items=None) # 迭代时保留原始插入顺序
4. Высокопроизводительное программирование и отладка
4.1 Выходные сообщения об ошибках и предупреждения
вывод информации в стандартную ошибку
import sys
sys.stderr.write('')
вывод предупреждающего сообщения
import warnings
warnings.warn(message, category=UserWarning)
# category的取值有DeprecationWarning, SyntaxWarning, RuntimeWarning, ResourceWarning, FutureWarning
Управление выводом предупреждающих сообщений
$ python -W all # 输出所有警告,等同于设置warnings.simplefilter('always')
$ python -W ignore # 忽略所有警告,等同于设置warnings.simplefilter('ignore')
$ python -W error # 将所有警告转换为异常,等同于设置warnings.simplefilter('error')
4.2 Тестирование в коде
Иногда для отладки мы хотим добавить в код некоторый код, обычно несколько операторов печати, которые можно записать так:
# 在代码中的debug部分
if __debug__:
pass
После завершения отладки эта часть кода игнорируется путем выполнения параметра -O в командной строке:
$ python -0 main.py
4.3 Проверка стиля кода
Используя pylint, можно выполнить большую проверку стиля кода и синтаксиса, а также найти некоторые ошибки перед запуском.
pylint main.py
4.4 Код, отнимающий много времени
длительный тест
$ python -m cProfile main.py
Требуется много времени для тестирования блока кода
# 代码块耗时定义
from contextlib import contextmanager
from time import perf_counter
@contextmanager
def timeblock(label):
tic = perf_counter()
try:
yield
finally:
toc = perf_counter()
print('%s : %s' % (label, toc - tic))
# 代码块耗时测试
with timeblock('counting'):
pass
Некоторые принципы трудоемкой оптимизации кода
- Сосредоточьтесь на оптимизации узких мест в производительности, а не всего кода.
- Избегайте использования глобальных переменных. Поиск локальных переменных выполняется быстрее, чем глобальных, а код, определяющий глобальные переменные в функции, обычно выполняется на 15–30 % быстрее.
- Избегайте использования свойств .access. Быстрее использовать имя импорта из модуля, чтобы поместить часто используемую переменную члена класса self.member в локальную переменную.
- По возможности используйте встроенные структуры данных. str, list, set, dict и т. д. реализованы на C и работают очень быстро.
- Избегайте создания ненужных промежуточных переменных и функции copy.deepcopy().
- Конкатенация строк, например, a + ':' + b + ':' + c создаст много бесполезных промежуточных переменных, ':', join([a, b, c]) будет намного эффективнее. Также необходимо учитывать, необходима ли конкатенация строк, например, print(':'.join([a, b, c])) менее эффективна, чем print(a, b, c, sep=':') .
5. Другие советы по Python
5.1 аргмин и аргмакс
items = [2, 1, 3, 4]
argmin = min(range(len(items)), key=items.__getitem__)
То же самое верно и для argmax.
5.2 Транспонирование двумерного списка
A = [['a11', 'a12'], ['a21', 'a22'], ['a31', 'a32']]
A_transpose = list(zip(*A)) # list of tuple
A_transpose = list(list(col) for col in zip(*A)) # list of list
5.3 Развертывание одномерного списка в двумерный список
A = [1, 2, 3, 4, 5, 6]
# Preferred.
list(zip(*[iter(A)] * 2))
Автор: Бэйцзян Айгуо