Распространенные ошибки в Python [дубликат]

это просто, например, если у вас есть этот объект массива:

 cars = ["Dodge", "Fiat", "Audi", "Volvo", "BMW", "Ford"];

и вы хотите, чтобы объект был отсортирован во фронте HTML, используйте:

 <li ng-repeat="x in cars | orderBy">{{x}}</li>

По умолчанию строки отсортированы в алфавитном порядке, а числа отсортированы по номерам.

Если у вас есть этот массив с ключами:

customers = [
{"name" : "Bottom-Dollar Marketse" ,"city" : "Tsawassen"},
{"name" : "Alfreds Futterkiste", "city" : "Berlin"},
{"name" : "Bon app", "city" : "Marseille"},
{"name" : "Cactus Comidas para llevar", "city" : "Buenos Aires"},
{"name" : "Bolido Comidas preparadas", "city" : "Madrid"},
{"name" : "Around the Horn", "city" : "London"},
{"name" : "B's Beverages", "city" : "London"}
];

Использование:

Сортировать массив по «городскому» порядку DESC:

<li ng-repeat="x in customers | orderBy : '-city'">{{x.name + ", " + x.city}}</li>

Сортировать массив по "городу":

<li ng-repeat="x in customers | orderBy : 'city'">{{x.name + ", " + x.city}}</li>
75
задан 20 revs, 9 users 32% 23 May 2017 в 10:30
поделиться

32 ответа

Избегайте использования ключевых слов в качестве собственных идентификаторов.

Кроме того, всегда хорошо не использовать из некоторого импорта модуля * .

14
ответ дан 24 November 2019 в 11:24
поделиться

Я бы прекратил использовать устаревшие методы в версии 2.6, чтобы ваше приложение или сценарий были готовы и их было легче преобразовать в Python 3.

6
ответ дан 24 November 2019 в 11:24
поделиться

Некоторые личные мнения, но я считаю, что лучше НЕ :

  • использовать устаревшие модули (используйте предупреждения для них)

  • чрезмерное использование классов и наследование (возможно, типичное для устаревших статических языков)

  • явно используют декларативные алгоритмы (как итерация с для по сравнению с использованием itertools )

  • переопределение функций из стандартной библиотеки, «потому что мне не нужны все эти функции»

  • с использованием функций ради них (снижение совместимости со старыми версиями Python)

  • с использованием метаклассов, когда вам действительно не нужно и, в более общем смысле, делать вещи слишком "волшебными"

  • избегайте использования генераторов

  • (более личных) попробуйте микрооптимизировать код CPython на низкоуровневой основе. Лучше потратьте время на алгоритмы, а затем оптимизируйте, создав небольшую разделяемую библиотеку C, вызываемую ctypes (так легко получить 5-кратное повышение производительности во внутреннем цикле)

  • , используйте ненужные списки, когда достаточно итераторов

  • ] запрограммируйте проект непосредственно для 3.x до того, как все необходимые библиотеки станут доступны (сейчас этот момент может быть немного спорным! )

4
ответ дан 24 November 2019 в 11:24
поделиться
import this    

Красивое лучше чем некрасиво.
Явное лучше, чем неявное.
Лучше простое, чем сложное.
Сложное лучше, чем сложное.
Плоский лучше, чем вложенный.
Разреженное лучше плотного.
Важна удобочитаемость.
Особых случаев недостаточно, чтобы нарушать правила.
Хотя практичность важнее чистоты.
Ошибки никогда не должны проходить незаметно.
Если явно не замолчать.
Перед лицом двусмысленности откажитесь от соблазна угадать.
Должен быть один - а желательно только один - очевидный способ сделать это.
Хотя сначала этот способ может быть не очевиден, если вы не голландец.
Сейчас лучше, чем никогда.
Хотя никогда не бывает лучше, чем прямо сейчас.
Если реализацию трудно объяснить, это плохая идея.
Если реализацию легко объяснить, это может быть хорошей идеей.
Пространства имен - одна отличная идея - давайте сделаем их больше!

import not_this

Напишите уродливый код.
Напишите неявный код.
Напишите сложный код.
Напишите вложенный код.
Пишите плотный код.
Напишите нечитаемый код.
Напишите особые случаи.
Стремитесь к чистоте.
Игнорировать ошибки и исключения.
Перед выпуском напишите оптимальный код.
Для каждой реализации нужна блок-схема.
Не используйте пространства имен.

4
ответ дан 24 November 2019 в 11:24
поделиться

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

>>> a=[[1,2,3,4,5]]*4

И, конечно же, она даст вам то, что вы ожидаете, когда вы посмотрите на нее

>>> from pprint import pprint
>>> pprint(a)

[[1, 2, 3, 4, 5],
 [1, 2, 3, 4, 5],
 [1, 2, 3, 4, 5],
 [1, 2, 3, 4, 5]]

Но не ожидайте, что элементы вашей популяции будут разделены объекты:

>>> a[0][0] = 2
>>> pprint(a)

[[2, 2, 3, 4, 5],
 [2, 2, 3, 4, 5],
 [2, 2, 3, 4, 5],
 [2, 2, 3, 4, 5]]

Если это не то, что вам нужно ...

Стоит упомянуть обходной путь:

a = [[1,2,3,4,5] for _ in range(4)]
35
ответ дан 24 November 2019 в 11:24
поделиться

Проблемы с языком Python - вещи, которые терпят неудачу очень неясными способами

  • Использование изменяемых аргументов по умолчанию.

  • Начальные нули означают восьмеричное число. 09 - очень непонятная синтаксическая ошибка в Python 2.x

  • Ошибочное написание переопределенных имен методов в суперклассе или подклассе. Ошибка неправильного написания суперкласса еще хуже, потому что ни один из подклассов не переопределяет ее правильно.

Проблемы проектирования Python

  • Тратить время на самоанализ (например, попытки автоматически определить типы или идентичность суперкласса или что-то другое). Во-первых, это очевидно из чтения первоисточника. Что еще более важно, время, потраченное на странный самоанализ Python, обычно указывает на фундаментальную неспособность понять полиморфизм. 80% вопросов самоанализа Python по SO - это неспособность получить полиморфизм.

  • Тратить время на код для гольфа. Тот факт, что ваша ментальная модель вашего приложения состоит из четырех ключевых слов («делаю», «что», «я», «имею в виду»), не означает, что вы должны создать для этого гипер-сложную интроспективную структуру, управляемую декоратором. Python позволяет поднять СУХОЙ до уровня глупости. Остальные вопросы самоанализа Python по SO пытаются свести сложные проблемы к кодированию упражнений в гольф.

  • Monkeypatching.

  • Неспособность на самом деле прочитать стандартную библиотеку и изобретать колесо.

  • Конфликт интерактивного типа как- вы идете на Python с подходящей программой. При вводе текста в интерактивном режиме вы можете потерять отслеживание переменной и использовать globals () . Кроме того, пока вы печатаете, почти все глобально. В правильных программах вы никогда не потеряете переменную, и ничего не будет глобальным.

28
ответ дан 24 November 2019 в 11:24
поделиться

В некоторой степени связано с изменяемым аргументом по умолчанию, то, как проверка на «отсутствующий» регистр приводит к различиям при передаче пустого списка:

def func1(toc=None):
    if not toc:
        toc = []
    toc.append('bar')

def func2(toc=None):
    if toc is None:
        toc = []
    toc.append('bar')

def demo(toc, func):
    print func.__name__
    print '  before:', toc
    func(toc)
    print '  after:', toc

demo([], func1)
demo([], func2)

Вот результат:

func1
  before: []
  after: []
func2
  before: []
  after: ['bar']
3
ответ дан 24 November 2019 в 11:24
поделиться

++ n и - n могут работать не так, как ожидалось людьми, работающими с C или Java.

++ n - это положительное значение положительного числа, которое просто n .

- n отрицательное значение отрицательного числа, которое просто n .

5
ответ дан 24 November 2019 в 11:24
поделиться

Плохая привычка, от которой мне пришлось избавиться, заключалась в использовании X и Y или Z для встроенной логики.

Если вы не можете на 100% гарантировать, что ] Y будет истинным значением, даже если ваш код изменится через 18 месяцев, вы настроите себя на какое-то неожиданное поведение.

К счастью, в более поздних версиях вы можете использовать Y if X else Z .

6
ответ дан 24 November 2019 в 11:24
поделиться

Последняя ссылка является исходной, этот вопрос SO является дубликатом.

7
ответ дан 24 November 2019 в 11:24
поделиться

Удивлен, что никто этого не сказал:

Смешивайте табуляцию и пробелы при отступах.

Действительно, это убийца. Поверь мне. В частности, , если он работает.

13
ответ дан 24 November 2019 в 11:24
поделиться

Я тоже начал изучать Python, и одна из самых больших ошибок, которые я сделал, - это постоянное использование C ++ / C #, индексированного "для" петля. В Python есть цикл типа for (i; i

Пример: У меня был метод, который перебирал список и возвращал индексы выбранных элементов:

for i in range(len(myList)):
    if myList[i].selected:
        retVal.append(i)

Вместо этого Python имеет понимание списка, которое решает ту же проблему более элегантным и легким для чтения способом:

retVal = [index for index, item in enumerate(myList) if item.selected]
5
ответ дан 24 November 2019 в 11:24
поделиться

Никогда не предполагайте, что наличие многопоточного приложения Python и машины с поддержкой SMP (например, оснащенной многоядерный процессор) даст вам преимущество внедрения настоящего параллелизма в ваше приложение. Скорее всего, этого не произойдет из-за GIL (Global Interpreter Lock), который синхронизирует ваше приложение на уровне интерпретатора байтового кода.

Есть некоторые обходные пути, например, использование SMP путем помещения параллельного кода в вызовы C API или использование нескольких процессов (вместо потоков) через оболочки (например, такой, который доступен по адресу http: //www.parallelpython .org ), но если вам нужна настоящая многопоточность в Python, нужно посмотреть на такие вещи, как Jython, IronPython и т. д.

4
ответ дан 24 November 2019 в 11:24
поделиться

Изменение аргумента по умолчанию:

def foo(bar=[]):
    bar.append('baz')
    return bar

Значение по умолчанию оценивается только один раз, а не каждый раз при вызове функции. Повторные вызовы foo () вернут ['baz'] , ['baz', 'baz'] , ['baz', 'baz', 'baz'] , ...

Если вы хотите изменить bar, сделайте что-то вроде этого:

def foo(bar=None):
    if bar is None:
        bar = []

    bar.append('baz')
    return bar

Или, если вы хотите, чтобы аргументы были окончательными:

def foo(bar=[]):
    not_bar = bar[:]

    not_bar.append('baz')
    return not_bar
26
ответ дан 24 November 2019 в 11:24
поделиться

Я не знаю, является ли это распространенной ошибкой, но хотя в Python нет операторов увеличения и уменьшения, двойные знаки разрешены, поэтому

++i

и

--i

синтаксически верны код, но не делает ничего «полезного» или ожидаемого.

21
ответ дан 24 November 2019 в 11:24
поделиться

Обычное копирование (назначение) выполняется по ссылке, поэтому заполнение контейнера путем адаптации одного и того же объекта и вставки заканчивается контейнером со ссылками на последний добавленный объект.

Используйте copy.deepcopy вместо.

9
ответ дан 24 November 2019 в 11:24
поделиться

Самая первая ошибка еще до того, как вы начнете: не бойтесь пробелов .

Когда вы показываете кому-то кусок кода Python, они впечатляются, пока вы говорите им, что они должны делать отступы правильно. По какой-то причине большинство людей считают, что язык не должен навязывать им определенный стиль, в то время как все они, тем не менее, сделают отступ в коде.

3
ответ дан 24 November 2019 в 11:24
поделиться

Распространенная ошибка: аргументы по умолчанию оцениваются один раз :

def x(a, l=[]):
    l.append(a)
    return l

print x(1)
print x(2)

выводит:

[1]
[1, 2]

т.е. вы всегда получаете один и тот же список.

3
ответ дан 24 November 2019 в 11:24
поделиться
my_variable = <something>
...
my_varaible = f(my_variable)
...
use my_variable and thinking it contains the result from f, and not the initial value

Python никоим образом не предупредит вас о том, что при втором назначении вы неправильно написали имя переменной и создали новое.

3
ответ дан 24 November 2019 в 11:24
поделиться

Not using functional tools. This isn't just a mistake from a style standpoint, it's a mistake from a speed standpoint because a lot of the functional tools are optimized in C.

This is the most common example:

temporary = []
for item in itemlist:
    temporary.append(somefunction(item))
itemlist = temporary

The correct way to do it:

itemlist = map(somefunction, itemlist)

The just as correct way to do it:

itemlist = [somefunction(x) for x in itemlist]

And if you only need the processed items available one at a time, rather than all at once, you can save memory and improve speed by using the iterable equivalents

# itertools-based iterator
itemiter = itertools.imap(somefunction, itemlist)
# generator expression-based iterator
itemiter = (somefunction(x) for x in itemlist)
12
ответ дан 24 November 2019 в 11:24
поделиться

Если вы пришли из C ++, имейте в виду, что переменные, объявленные в определении класса, являются статическими. Вы можете инициализировать нестатические члены в методе init .

Пример:

class MyClass:
  static_member = 1

  def __init__(self):
    self.non_static_member = random()
13
ответ дан 24 November 2019 в 11:24
поделиться

Не используйте индекс для перебора последовательности

Не делайте:

for i in range(len(tab)) :
    print tab[i]

Делайте:

for elem in tab :
    print elem

Ибо автоматизирует для вас большинство операций итераций.

Используйте enumerate , если вам действительно нужны и индекс, и элемент.

for i, elem in enumerate(tab):
     print i, elem

Будьте осторожны при использовании «==» для проверки на True или False

if (var == True) :
    # this will execute if var is True or 1, 1.0, 1L

if (var != True) :
    # this will execute if var is neither True nor 1

if (var == False) :
    # this will execute if var is False or 0 (or 0.0, 0L, 0j)

if (var == None) :
    # only execute if var is None

if var :
    # execute if var is a non-empty string/list/dictionary/tuple, non-0, etc

if not var :
    # execute if var is "", {}, [], (), 0, None, etc.

if var is True :
    # only execute if var is boolean True, not 1

if var is False :
    # only execute if var is boolean False, not 0

if var is None :
    # same as var == None

Не проверьте, можете ли вы, просто сделайте это и обработайте ошибку

Питонисты обычно говорят: «Проще просить прощения, чем разрешения».

Не делать:

if os.path.isfile(file_path) :
    file = open(file_path)
else :
    # do something

Делать:

try :
    file =  open(file_path)
except OSError as e:
    # do something

Или даже лучше с python 2.6 + / 3:

with open(file_path) as file :

Он намного лучше, потому что он гораздо более универсален. Вы можете применить «попробовать / кроме» практически ко всему. Вам не нужно заботиться о том, что делать, чтобы предотвратить это, только об ошибке, которой вы рискуете.

Не проверять по типу

Python динамически типизируется, поэтому проверка типа приводит к потере гибкости. Вместо этого используйте утиный ввод, проверяя поведение. Например, вы ожидаете строку в функции, а затем используете str () для преобразования любого объекта в строку. Вы ожидаете список, используйте list () для преобразования любой итерации в списке.

Не делать:

def foo(name) :
    if isinstance(name, str) :
        print name.lower()

def bar(listing) :
    if isinstance(listing, list) :
        listing.extend((1, 2, 3))
        return ", ".join(listing)

Делать:

def foo(name) :
    print str(name).lower()

def bar(listing) :
    l = list(listing)
    l.extend((1, 2, 3))
    return ", ".join(l)

Используя последний способ, foo примет любой объект. Bar принимает строки, кортежи, наборы, списки и многое другое. Cheap DRY: -)

Не смешивайте пробелы и табуляции

Только не надо. Вы бы заплакали.

Используйте объект в качестве первого родителя

Это сложно, но он укусит вас по мере роста вашей программы. В Python 2.x есть старые и новые классы. Старые, ну, старые. У них отсутствуют некоторые функции, и они могут вести себя неловко с наследованием. Чтобы использовать любой из ваших классов, он должен соответствовать «новому стилю». Для этого сделайте его наследовать от "

8
ответ дан 24 November 2019 в 11:24
поделиться

Rolling your own code before looking in the standard library. For example, writing this:

def repeat_list(items):
    while True:
        for item in items:
            yield item

When you could just use this:

from itertools import cycle

Examples of frequently overlooked modules (besides itertools) include:

  • optparse for creating command line parsers
  • ConfigParser for reading configuration files in a standard manner
  • tempfile for creating and managing temporary files
  • shelve for storing Python objects to disk, handy when a full fledged database is overkill
16
ответ дан 24 November 2019 в 11:24
поделиться

Не используйте индекс для перебора последовательности

Не делайте:

for i in range(len(tab)) :
    print tab[i]

Делайте:

for elem in tab :
    print elem

Ибо автоматизирует большинство операций итераций для вас.

Используйте enumerate , если вам действительно нужны и индекс, и элемент.

for i, elem in enumerate(tab):
     print i, elem

Будьте осторожны при использовании «==» для проверки на True или False

if (var == True) :
    # this will execute if var is True or 1, 1.0, 1L

if (var != True) :
    # this will execute if var is neither True nor 1

if (var == False) :
    # this will execute if var is False or 0 (or 0.0, 0L, 0j)

if (var == None) :
    # only execute if var is None

if var :
    # execute if var is a non-empty string/list/dictionary/tuple, non-0, etc

if not var :
    # execute if var is "", {}, [], (), 0, None, etc.

if var is True :
    # only execute if var is boolean True, not 1

if var is False :
    # only execute if var is boolean False, not 0

if var is None :
    # same as var == None

Не проверьте, можете ли вы, просто сделайте это и обработайте ошибку.

Питонисты обычно говорят: «Проще просить прощения, чем разрешения».

Нельзя:

if os.path.isfile(file_path) :
    file = open(file_path)
else :
    # do something

Делать:

try :
    file =  open(file_path)
except OSError as e:
    # do something

Или даже лучше с python 2.6 + / 3:

with open(file_path) as file :

Это намного лучше, потому что это гораздо более универсально. Вы можете применить «попробовать / кроме» практически ко всему. Вам не нужно заботиться о том, что делать, чтобы предотвратить это, только об ошибке, которой вы рискуете.

Не проверять по типу

Python динамически типизирован, поэтому проверка на тип приводит к потере гибкости. Вместо этого используйте утиный ввод, проверяя поведение. Например, вы ожидаете строку в функции, а затем используете str () для преобразования любого объекта в строку. Вы ожидаете список, используйте list () для преобразования любой итерации в списке.

Не делать:

def foo(name) :
    if isinstance(name, str) :
        print name.lower()

def bar(listing) :
    if isinstance(listing, list) :
        listing.extend((1, 2, 3))
        return ", ".join(listing)

Делать:

def foo(name) :
    print str(name).lower()

def bar(listing) :
    l = list(listing)
    l.extend((1, 2, 3))
    return ", ".join(l)

Используя последний способ, foo примет любой объект. Bar принимает строки, кортежи, наборы, списки и многое другое. Дешевый СУХОЙ: -)

Не смешивайте пробелы и табуляции

Только не надо. Вы бы заплакали.

Используйте объект в качестве первого родителя

Это сложно, но он укусит вас по мере роста вашей программы. В Python 2.x есть старые и новые классы. Старые, ну, старые. У них отсутствуют некоторые функции, и они могут вести себя неловко с наследованием. Чтобы можно было использовать, любой из вашего класса должен быть «нового стиля». Для этого сделайте его наследованием от "объекта":

Не делать:

class Father :
    pass

class Child(Father) :
    pass

Делать:

class Father(object) :
    pass


class Child(Father) :
    pass

В Python 3.x все классы являются новым стилем, поэтому вы можете объявить отец класса:

Не инициализируйте атрибуты класса вне метода __ init __

Люди, пришедшие с других языков, находят это заманчивым, потому что то, что вы делаете на Java или PHP. Вы пишете имя класса, затем перечисляете свои атрибуты и присваиваете им значение по умолчанию. Похоже, что это работает в Python, однако это не работает так, как вы думаете.

В результате будут установлены атрибуты класса (статические атрибуты), а затем, когда вы попытаетесь получить атрибут объекта, он выдаст вам его значение если он не пуст. В этом случае он вернет атрибуты класса.

Это подразумевает две большие опасности:

  • Если атрибут класса изменяется, то изменяется начальное значение.
  • Если вы установите изменяемый объект в качестве значения по умолчанию, вы получите один и тот же объект, совместно используемый экземплярами.

Не делайте этого (если вы не хотите статического):

class Car(object):
    color = "red"
    wheels = [wheel(), Wheel(), Wheel(), Wheel()]

Выполните:

class Car(object):
    def __init__(self):
        self.color = "red"
        self.wheels = [wheel(), Wheel(), Wheel(), Wheel()]
72
ответ дан 24 November 2019 в 11:24
поделиться

Вы упомянули аргументы по умолчанию.... Тот, который почти так же плох, как и мутируемые аргументы по умолчанию: значения по умолчанию, которыми не являются None.

Рассмотрим функцию, которая будет готовить какую-нибудь еду:

def cook(breakfast="spam"):
    arrange_ingredients_for(breakfast)
    heat_ingredients_for(breakfast)
    serve(breakfast)

Так как она задает значение по умолчанию для завтрака , то для какой-нибудь другой функции невозможно сказать "готовить ваш завтрак по умолчанию" без специального случая:

def order(breakfast=None):
    if breakfast is None:
        cook()
    else:
        cook(breakfast)

Однако, этого можно было бы избежать, если бы cook использовала None в качестве значения по умолчанию:

def cook(breakfast=None):
    if breakfast is None:
        breakfast = "spam"

def order(breakfast=None):
    cook(breakfast)

Хорошим примером этого является ошибка Django #6988. В кэширующем модуле Django была функция "save to cache", которая выглядела следующим образом:

def set(key, value, timeout=0):
    if timeout == 0:
        timeout = settings.DEFAULT_TIMEOUT
    _caching_backend.set(key, value, timeout)

Но для memcached backend таймаут 0 означает "никогда не таймаут"... Который, как видите, было бы невозможно указать.

.
3
ответ дан 24 November 2019 в 11:24
поделиться

Использование средства форматирования % s в сообщениях об ошибках. Практически во всех случаях следует использовать % r .

Например, представьте себе такой код:

try:
    get_person(person)
except NoSuchPerson:
    logger.error("Person %s not found." %(person))

Напечатал эту ошибку:

ERROR: Person wolever not found.

Невозможно определить, является ли переменная person строкой "wolever" , юникодом string u "wolever" или экземпляр класса Person (который имеет __ str __ , определенный как def __str __ (self): return self.name ). Принимая во внимание, что если бы использовался % r , было бы три разных сообщения об ошибках:

...
logger.error("Person %r not found." %(person))

вызвали бы гораздо более полезные ошибки:

ERROR: Person 'wolever' not found.
ERROR: Person u'wolever' not found.
ERROR: Person  not found.

Еще одна веская причина для этого - то, что пути намного проще копировать вставить. Представьте:

try:
    stuff = open(path).read()
except IOError:
    logger.error("Could not open %s" %(path))

Если путь - это какой-то путь / со «странными» «символами» , сообщение об ошибке будет:

ERROR: Could not open some path/with 'strange' "characters"

Что трудно визуально проанализировать и трудно скопировать / вставить в оболочку.

Принимая во внимание, что если использовать % r , ошибка будет такой:

ERROR: Could not open 'some path/with \'strange\' "characters"'

Легко визуально анализировать, легко копировать и вставлять, все вокруг лучше.

7
ответ дан 24 November 2019 в 11:24
поделиться

Создание локального модуля с тем же именем, что и модуль из stdlib. Это почти всегда происходит случайно (как описано в , этот вопрос ), но обычно приводит к появлению загадочных сообщений об ошибках.

1
ответ дан 24 November 2019 в 11:24
поделиться

Не изменяйте список во время итерации по нему.

odd = lambda x : bool(x % 2)
numbers = range(10)
for i in range(len(numbers)):
    if odd(numbers[i]):
        del numbers[i]

Одно из распространенных предложений для обхода этой проблемы заключается в обратном итерационном просмотре списка:

for i in range(len(numbers)-1,0,-1):
    if odd(numbers[i]):
        del numbers[i]

Но еще лучше использовать понимание списка для построения нового списка взамен старого:

numbers[:] = [n for n in numbers if not odd(n)]
3
ответ дан 24 November 2019 в 11:24
поделиться

Similar to mutable default arguments is the mutable class attribute.

>>> class Classy:
...    foo = []
...    def add(self, value):
...        self.foo.append(value)
... 
>>> instance1 = Classy()
>>> instance2 = Classy()
>>> instance1.add("Foo!")
>>> instance2.foo
['Foo!']

Not what you expect.

0
ответ дан 24 November 2019 в 11:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: