Несколько интересных трюков с питоном

Python

заглавие | python-is-cool

автор | chiphuyen

оригинальный | GitHub.com/Бедные тоже могут/бояться…

переводчик| kbsc13 (автор публичного аккаунта «The Growth of Algorithm Apes»)

утверждение| Перевод предназначен для общения и обучения, добро пожаловать на перепечатку, но, пожалуйста, сохраните эту статью для коммерческих или незаконных целей.

Управляемое чтение

Эта статья в основном знакомит с некоторыми навыками Python.

Используемая версия Python3.6+

Содержание этой статьи следующее:

  • Lambda, map, filter, reduce
  • Список операций
  • Классы и магические методы
  • Свойства локальных пространств имен и объектов
  • сумасшедший импорт

1. Lambda, map, filter, reduce

lambdaзаключается в создании анонимной функции, ниже приведен пример использования, гдеsquare_fnиsquare_ldОбе эти функции делают одно и то же:

def square_fn(x):
    return x * x

square_ld = lambda x: x * x

for i in range(10):
    assert square_fn(i) == square_ld(i)

Из-за характеристик быстрого объявления,lambdaОтлично подходит для использования в функциях обратного вызова и в качестве аргумента для других функций. Кроме того, он также можетmap, filter , reduceИспользуйте эти функции вместе.

map(fn, iterable)будетiterableВсе элементы параметра передаются в функциюfn, который можно использовать здесь какiterableПараметры — это списки, наборы, словари, кортежи и строки, а возвращаемое значение — этоmapобъекта, пример выглядит следующим образом:

nums = [1/3, 333/7, 2323/2230, 40/34, 2/3]
nums_squared = [num * num for num in nums]
print(nums_squared)

==> [0.1111111, 2263.04081632, 1.085147, 1.384083, 0.44444444]

Если вы используетеmapфункция как функция обратного вызова, код:

nums_squared_1 = map(square_fn, nums)
nums_squared_2 = map(lambda x: x * x, nums)
print(list(nums_squared_1))

==> [0.1111111, 2263.04081632, 1.085147, 1.384083, 0.44444444]

Также можно использовать несколько итерируемых объектов, например, если вы хотите вычислить простую линейную функцию.f(x)=ax+bи настоящие этикеткиlabelsСреднеквадратическая ошибка , есть две реализации одного и того же эффекта:

a, b = 3, -0.5
xs = [2, 3, 4, 5]
labels = [6.4, 8.9, 10.9, 15.3]

# Method 1: using a loop
errors = []
for i, x in enumerate(xs):
    errors.append((a * x + b - labels[i]) ** 2)
result1 = sum(errors) ** 0.5 / len(xs)

# Method 2: using map
diffs = map(lambda x, y: (a * x + b - y) ** 2, xs, labels)
result2 = sum(diffs) ** 0.5 / len(xs)

print(result1, result2)

==> 0.35089172119045514 0.35089172119045514

должны знать о том,mapиfilterВсе возвращаемые объекты являются итераторами, а это значит, что их значения не сохраняются, а генерируются только при необходимости, поэтому, если вы вызоветеsum(diffs),diffsстанет пустым, если вы хотите сохранить всеdiffsЭлементы , должны быть преобразованы в тип списка --list(diffs).

filter(fn, iterable)как использовать иmapто же самое, разницаfnвозвращает логическое значение, затемfilterФункция возвращаетfnвернусьTrueэлемент, пример показан ниже:

bad_preds = filter(lambda x: x > 0.5, errors)
print(list(bad_preds))

==> [0.8100000000000006, 0.6400000000000011]

reduce(fn, iterable, initializer)это использование оператора, когда мы хотим перебрать элементы списка. Например, мы хотим вычислить произведение всех элементов списка:

product = 1
for num in nums:
    product *= num
print(product)

==> 12.95564683272412

Это эквивалентно:

from functools import reduce
product = reduce(lambda x, y: x * y, nums)
print(product)

==> 12.95564683272412

Уведомление:

lambdaВремя работы функции не очень хорошее, и использованиеdefЭто немного медленнее, чем определенная функция с именем, поэтому рекомендуется использовать функцию с именем.


2. Список операций

pythonВ списке также много специальных трюков.

2.1 Unpacking

Для каждого элемента развернутого списка это можно сделать:

elems = [1, 2, 3, 4]
a, b, c, d = elems
print(a, b, c, d)

==> 1 2 3 4

Вы также можете сделать это:

a, *new_elems, d = elems
print(a)
print(new_elems)
print(d)

==> 1
    [2, 3]
    4
2.2 Slicing

Перевернуть список можно путем нарезки --[::-1]:

elems = list(range(10))
print(elems)

==> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

print(elems[::-1])

==> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

этот синтаксис[x:y:z]представлены в виде списка, начиная с индексаxприбытьyэлементы вынесены из , размер шагаz. когдаzявляется отрицательным числом, это означает задом наперед,xЕсли не указано, по умолчанию выполняется обход списка с первого элемента. Если не указаноy, последний элемент используется по умолчанию. Итак, если мы хотим сэмплировать каждые 2 элемента, мы можем использовать[::2]:

evens = elems[::2]
print(evens)

reversed_evens = elems[-2::-2]
print(reversed_evens)

==> [0, 2, 4, 6, 8]
    [8, 6, 4, 2, 0]

Вы также можете удалить элементы списка путем нарезки:

del elems[::2]
print(elems)

==> [1, 3, 5, 7, 9]
2.3 Insertion

Реализация кода для изменения элемента в списке выглядит так:

elems = list(range(10))
elems[1] = 10
print(elems)

==> [0, 10, 2, 3, 4, 5, 6, 7, 8, 9]

И если вы хотите изменить несколько элементов в определенном диапазоне, например, с 3 значениями20,30,40заменить значение1, код выглядит так:

elems = list(range(10))
elems[1:2] = [20, 30, 40]
print(elems)

==> [0, 20, 30, 40, 2, 3, 4, 5, 6, 7, 8, 9]

Вы также можете вставить 3 значения между индексом 0 и индексом 1[0.2, 0.3, 0.5]:

elems = list(range(10))
elems[1:1] = [0.2, 0.3, 0.5]
print(elems)

==> [0, 0.2, 0.3, 0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2.4 Flattening

принявsumметод для выравнивания вложенного списка объектов:

list_of_lists = [[1], [2, 3], [4, 5, 6]]
sum(list_of_lists, [])

==> [1, 2, 3, 4, 5, 6]

Но если уровней вложенности слишком много, требуются рекурсивные операции.Вот еще один способ передатьlambdaРеализованный метод:

nested_lists = [[1, 2], [[3, 4], [5, 6], [[7, 8], [9, 10], [[11, [12, 13]]]]]]
flatten = lambda x: [y for l in x for y in flatten(l)] if type(x) is list else [x]
flatten(nested_lists)

# This line of code is from
# https://github.com/sahands/python-by-example/blob/master/python-by-example.rst#flattening-lists
2.5 List vs generator

Чтобы объяснить разницу между списками и генераторами, вот пример n-грамм всех строк, которые создают список:

Один из способов сделать это — использовать скользящее окно:

tokens = ['i', 'want', 'to', 'go', 'to', 'school']

def ngrams(tokens, n):
    length = len(tokens)
    grams = []
    for i in range(length - n + 1):
        grams.append(tokens[i:i+n])
    return grams

print(ngrams(tokens, 3))

==> [['i', 'want', 'to'],
     ['want', 'to', 'go'],
     ['to', 'go', 'to'],
     ['go', 'to', 'school']]

В приведенном выше примере нам нужно сохранить всеn-grams, если текст естьmсимволов, то объем памятиO(nm), который вmЭто большая проблема, когда она большая.

Поэтому вы можете рассмотреть возможность создания новых, когда это необходимо, с помощью генератора.n-gram, поэтому мы можем создать функциюngramsпо ключевому словуyieldВозвращает генератор, эта память нужна толькоO(m+n):

def ngrams(tokens, n):
    length = len(tokens)
    for i in range(length - n + 1):
        yield tokens[i:i+n]

ngrams_generator = ngrams(tokens, 3)
print(ngrams_generator)

==> <generator object ngrams at 0x1069b26d0>

for ngram in ngrams_generator:
    print(ngram)

==> ['i', 'want', 'to']
    ['want', 'to', 'go']
    ['to', 'go', 'to']
    ['go', 'to', 'school']

Другой способ генерацииn-gramsзаключается в создании списка путем нарезки[0, 1, ..., -n], [1, 2, ..., -n+1], ..., [n-1, n, ..., -1], то черезzipупаковать вместе:

def ngrams(tokens, n):
    length = len(tokens)
    slices = (tokens[i:length-n+i+1] for i in range(n))
    return zip(*slices)

ngrams_generator = ngrams(tokens, 3)
print(ngrams_generator)

==> <zip object at 0x1069a7dc8> # zip objects are generators

for ngram in ngrams_generator:
    print(ngram)

==> ('i', 'want', 'to')
    ('want', 'to', 'go')
    ('to', 'go', 'to')
    ('go', 'to', 'school')

Обратите внимание, что способ создания фрагментов здесь(tokens[...] for i in range(n)), вместо[tokens[...] for i in range(n)],так как[]представляет собой понимание списка, и()вернет генератор.


3. Классы и магические методы

В python метод magic имеет префикс и суффикс с двумя символами подчеркивания.__, наверное, один из самых известных магических методов__init__, следующее заключается в реализацииNodeкласс, представляющий бинарное дерево:

class Node:
    """ A struct to denote the node of a binary tree.
    It contains a value and pointers to left and right children.
    """
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

Если бы мы напечаталиNodeобъект, но вывод не очень интерпретируемый:

root = Node(5)
print(root) # <__main__.Node object at 0x1069c4518>

В идеале вы можете вывести значение узла и всех содержащихся в нем дочерних узлов.Для достижения этой функции вы можете использовать__repr__метод, который возвращает интерпретируемый объект, например строку.

class Node:
    """ A struct to denote the node of a binary tree.
    It contains a value and pointers to left and right children.
    """
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

    def __repr__(self):
        strings = [f'value: {self.value}']
        strings.append(f'left: {self.left.value}' if self.left else 'left: None')
        strings.append(f'right: {self.right.value}' if self.right else 'right: None')
        return ', '.join(strings)

left = Node(4)
root = Node(5, left)
print(root) # value: 5, left: 4, right: None

Далее мы можем захотеть дополнительно реализовать функцию сравнения значений двух узлов, здесь__eq__добиться равенства==,__lt__достичь меньше, чем<,__ge__достичь большего или равного>=.

class Node:
    """ A struct to denote the node of a binary tree.
    It contains a value and pointers to left and right children.
    """
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

    def __eq__(self, other):
        return self.value == other.value

    def __lt__(self, other):
        return self.value < other.value

    def __ge__(self, other):
        return self.value >= other.value


left = Node(4)
root = Node(5, left)
print(left == root) # False
print(left < root) # True
print(left >= root) # False

В следующей статье приведен список всех магических методов:

woohoo.tutorials учитель.com/Python/magi…

Конечно, вы также можете проверить инструкции в официальной документации, но это будет немного сложно читать:

docs.Python.org/3/reference…

Рекомендуются следующие методы:

  • __len__: переписатьlen()метод
  • __str__: переписатьstr()метод
  • __iter__: Если вы хотите, чтобы объект был итерируемым, вы можете наследовать этот метод, а также можете вызыватьnext()метод

для подобныхNodeТакой класс, для которого мы определяем все свойства, которые он поддерживает (например, дляNode, здесь означаетvalue, left, rightс тремя свойствами), вы можете использовать__slots__для представления этих значений, что полезно для повышения производительности и экономии места в памяти. хотите знать больше__slots__, ознакомьтесь с этим ответом Stackoverflow:

stackoverflow.com/questions/4…

class Node:
    """ A struct to denote the node of a binary tree.
    It contains a value and pointers to left and right children.
    """
    __slots__ = ('value', 'left', 'right')
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

4. Локальное пространство имен, свойства объекта

locals()Функция вернет словарь, содержащий все переменные, определенные в локальном пространстве имен, как показано в следующем примере:

class Model1:
    def __init__(self, hidden_size=100, num_layers=3, learning_rate=3e-4):
        print(locals())
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        self.learning_rate = learning_rate

model1 = Model1()

==> {'learning_rate': 0.0003, 'num_layers': 3, 'hidden_size': 100, 'self': <__main__.Model1 object at 0x1069b1470>}

Все свойства объекта хранятся в его__dict__:

print(model1.__dict__)

==> {'hidden_size': 100, 'num_layers': 3, 'learning_rate': 0.0003}

Обратите внимание, что ручное назначение всех параметров соответствующим свойствам может быть громоздким, особенно если список параметров велик. Чтобы избежать этого, объект__dict__:

class Model2:
    def __init__(self, hidden_size=100, num_layers=3, learning_rate=3e-4):
        params = locals()
        del params['self']
        self.__dict__ = params

model2 = Model2()
print(model2.__dict__)

==> {'learning_rate': 0.0003, 'num_layers': 3, 'hidden_size': 100}

если объект передан**kwargsДля инициализации будет удобнее, но**kwargsВы должны использовать как можно меньше:

class Model3:
    def __init__(self, **kwargs):
        self.__dict__ = kwargs

model3 = Model3(hidden_size=100, num_layers=3, learning_rate=3e-4)
print(model3.__dict__)

==> {'hidden_size': 100, 'num_layers': 3, 'learning_rate': 0.0003}

5. Безумный импорт

Обычно попадают в эту сумасшедшую операцию по импорту*Пример показан ниже:

существуетfile.pyв файле

from parts import *

Такой способ написания очень безответственный, он импортирует все из другого модуля в текущий модуль, включая импортированное содержимое этого модуля.Например,parts.pyМодуль может выглядеть так:

import numpy
import tensorflow

class Encoder:
    ...

class Decoder:
    ...

class Loss:
    ...

def helper(*args, **kwargs):
    ...

def utils(*args, **kwargs):
    ...

так какparts.pyне определен__all__,такfile.pyбудет импортироватьEncoder, Decoder, Loss, utils, helper,а такжеnumpyиtensorflow.

если мы просто хотимEncoder, Decoder, Lossимпортируется в другой модуль для использования, то вам нужно указать__all__параметр:

 __all__ = ['Encoder', 'Decoder', 'Loss']
import numpy
import tensorflow

class Encoder:
    ...

Через приведенный выше код, когда есть другой файл, он также напрямую используетсяfrom part import *подход, то только данныйEncoder, Decoder, Loss,в то же время__all__Также обзор модуля.


Ссылаться на


Добро пожаловать в мой общедоступный аккаунт WeChat--Рост алгоритма обезьяны, или отсканируйте QR-код ниже, чтобы общаться, учиться и развиваться вместе!

Рекомендуемое чтение: