Comments 27
Напрашивается, что использование коллекций противоречит «Простое лучше сложного». Нет?
+1
Ага, как и пункт про
Ellipsis
. На самом деле слишком поверхностная заметка, есть куда более интересная Стиль кода в языке python via. http://habrahabr.ru/post/76738/+3
Добавил в заметки. Лучше периодически подобные статьи просматривать, чтобы не написать того, о чем потом будет стыдно :)
0
Все что полезно описано в любой книге, а что не описано вызывает смешанные чувства. Например это:
>> Создавая классы в Python задействуйте magic methods
??, так ли это обязательно?
или это
>>Обратите внимание что вы можете использовать выражение if...else в любом выражение.
Это тоже врядли добавит читабельности
>> Создавая классы в Python задействуйте magic methods
??, так ли это обязательно?
или это
>>Обратите внимание что вы можете использовать выражение if...else в любом выражение.
Это тоже врядли добавит читабельности
+1
>>Обратите внимание что вы можете использовать выражение if...else в любом выражение.
Это тоже врядли добавит читабельности
Это гораздо читабельнее, чем тернарный условный оператор в других языках. Я при чтении кода всегда спотыкаюсь о конструкции вида a?b:c, потому что там, конечно, не a, b и c стоят, а что-нибудь посложнее. А так гораздо симпатичнее.
Плюс применение в форме
(func1 if y == 1 else func2)(arg1, arg2)
выглядит вполне прилично и, что гораздо важнее, естественно. Обычно в таких ситуациях надо писать вилку из условий или делегировать выбор вызываемой функции в другую функцию. Если первое вполне ничего (и в большинстве случаев, конечно, так и надо писать), то второе и нужно редко, и читать неудобно (и незачем плодить сущности, кроме того).+1
На счёт скорости словарей:
7.2616994618
3.58429660753
3.52491727301
2.65262847652
# coding: utf-8
from collections import defaultdict
def trydict():
freqs = {}
for c in "abracadabra":
try:
freqs[c] += 1
except:
freqs[c] = 1
def getdict():
freqs = {}
for c in "abracadabra":
freqs[c] = freqs.get(c, 0) + 1
def defdict():
freqs = defaultdict(int)
for c in "abracadabra":
freqs[c] += 1
def indict():
freqs = {}
for c in "abracadabra":
freqs[c] = freqs[c] + 1 if c in freqs else 1
if __name__ == '__main__':
from timeit import Timer
t = Timer("trydict()", "from __main__ import trydict")
print t.timeit()
t = Timer("getdict()", "from __main__ import getdict")
print t.timeit()
t = Timer("defdict()", "from __main__ import defdict")
print t.timeit()
t = Timer("indict()", "from __main__ import indict")
print t.timeit()
7.2616994618
3.58429660753
3.52491727301
2.65262847652
+3
А это:
Хоть и красиво, ещё медленнее, чем с try (около 10, относительно этих замеров).
c = Counter("abracadabra")
Хоть и красиво, ещё медленнее, чем с try (около 10, относительно этих замеров).
+1
Интересно, спасибо. Решил сравнить с PyPy:
➜ ~ time python dict_speed.py
6.94295406342
4.35391998291
4.43011784554
3.0556640625
python dict_speed.py 18,81s user 0,02s system 99% cpu 18,828 total
➜ ~ time pypy dict_speed.py
0.70256114006
0.649827003479
0.807382106781
0.672923088074
pypy dict_speed.py 2,87s user 0,02s system 99% cpu 2,893 total
+7
Забавно, но следующий вариант просто рвет остальных конкурсантов по скорости:
Результат: 0.0511064953786
что я делаю не так?
def mydict():
freqs = {}
for c in "abracadabra":
if freqs.has_key(c):
freqs[c]+=1
else:
freqs[c]=1
Результат: 0.0511064953786
что я делаю не так?
0
У меня есть небольшое замечание, я обычно для проверки чёртности/нечётности использую не
Первый:
Второй:
И далее выпонение:
и
num % 2
, а num & 1
. Решил протестировать на интерпретаторе Python 2.7.3. Скорость выполнения моего вариант с & оказался чуточку быстрее, чем %. Написал я два скрипта следующих:Первый:
# odd1.py
numbers = range(30000000)
numbers = [num for num in numbers if not num & 1]
Второй:
# odd2.py
numbers = range(30000000)
numbers = [num for num in numbers if not num % 2]
И далее выпонение:
time python odd1.py
real 0m4.704s
user 0m4.228s
sys 0m0.388s
и
time python odd2.py
real 0m5.122s
user 0m4.708s
sys 0m0.376s
+2
По поводу
Кроме того, есть ещё built-in функция reversed, которая возвращает итератор, идущий в обратном направлении по тому же списку. Были ещё всякие reviter, ireverse, inreverse, но сейчас ими никто не пользуется вроде.
x.reverse()
есть одна опасность, которая по невнимательности может насолить. reverse()
является методом списка и изменяет сам список, а не только возвращает перевёрнутую копию.Кроме того, есть ещё built-in функция reversed, которая возвращает итератор, идущий в обратном направлении по тому же списку. Были ещё всякие reviter, ireverse, inreverse, но сейчас ими никто не пользуется вроде.
+1
> и изменяет сам список, а не только возвращает перевёрнутую копию.
Не возвращает он ничего. Возвращает функция reversed(), а не метод списка.
Не возвращает он ничего. Возвращает функция reversed(), а не метод списка.
+1
Ну вот, ещё того хуже.
Совсем разные вещи получаются.
Хотя, с другой стороны, так даже стройнее: одно для изменения на месте, одно для изменённой копии, одно для итератора — никаких пересечений и дублирования.
Частный случай x[::-1] является средством выражения x.reverse()
Совсем разные вещи получаются.
Хотя, с другой стороны, так даже стройнее: одно для изменения на месте, одно для изменённой копии, одно для итератора — никаких пересечений и дублирования.
0
reversed и срезы не то чтобы пересекаются — они работают с разными «интерфейсами» объекта.
Срез работает через метод __getitem__() объекта, причем этот метод должен уметь принимать экземпляр slice в качестве параметра.
reversed() работает только с объектами, которые имеют и __getitem__() и __len__(), но зато reversed() работает поэлементно и делает это лениво (итератор). При этом reversed() вызывает __getitem__() с целочисленным аргументом.
Таким образом, reversed(), в общем случае, предполагает, что источником данных для него будет объект-контейнер с произвольным доступом к содержимому.
А срез можно реализовать и для объектов, представляющих потоковые данные — через пропуски ненужных элементов. Правда без буферизации не будут работать отрицательные шаги и срез от конца. Так работает itertools.islice — итератор-срез от итерируемого источника
Ну и reversed()/срезы, это, так сказать, функциональный подход — результат отвязан от источника.
А reverse(), это ООП подход — метод объекта, изменяющий его состояние.
Срез работает через метод __getitem__() объекта, причем этот метод должен уметь принимать экземпляр slice в качестве параметра.
reversed() работает только с объектами, которые имеют и __getitem__() и __len__(), но зато reversed() работает поэлементно и делает это лениво (итератор). При этом reversed() вызывает __getitem__() с целочисленным аргументом.
Таким образом, reversed(), в общем случае, предполагает, что источником данных для него будет объект-контейнер с произвольным доступом к содержимому.
А срез можно реализовать и для объектов, представляющих потоковые данные — через пропуски ненужных элементов. Правда без буферизации не будут работать отрицательные шаги и срез от конца. Так работает itertools.islice — итератор-срез от итерируемого источника
Ну и reversed()/срезы, это, так сказать, функциональный подход — результат отвязан от источника.
А reverse(), это ООП подход — метод объекта, изменяющий его состояние.
+2
«Вкратце этот дизайн иногда встречается.»
«Использование многоточие для получения всех элементов, является нарушением принципа „Только Один Путь Достижения Цели“.»
«Хотя в какой то момент оно идет слишком далеко.»
Ад.
«Использование многоточие для получения всех элементов, является нарушением принципа „Только Один Путь Достижения Цели“.»
«Хотя в какой то момент оно идет слишком далеко.»
Ад.
+3
Существует ряд других магических методов.кто-нибудь объясните, почему
>>> from datetime import date, timedelta
>>> 1 + 1
2
>>> date(2012, 01, 01) + timedelta(days=1)
datetime.date(2012, 1, 2)
но
>>> range(0, 3, 1)
[0, 1, 2]
>>> range(date(2012,01,01), date(2012,01,03), timedelta(days=1))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: range() integer end argument expected, got datetime.date.
-1
А как связана range() с магическими методами?
Касательно магии сложения/вычитания: обычно операции сложения/вычитания работают над объектами одного типа (если для их класса реализовано соответствующее поведение), а datetime/timedelta, это скорее исключение: нельзя сложить две даты — это лишено смысла, но можно изменить дату на некоторое кол-во фиксированных единиц (дней/часов/минут...) и нельзя увеличить дату на n месяцев/лет — кол-во дней плавает.
range() работает только с числами и «магию» не использует (за пределами операций над числами).
При этом её аргумент «шаг» — необязателен. А как тогда как ф-ция должна определять, чем инкрементить дату?
Можно написать всеядную «магическую» версию range() (я напишу «ленивый» вариант):
Этот вариант работать будет для дат/времени (да и для списков, скажем), но потребует обязательного указания объекта-инкремента. Такое поведение неявно, ИМХО.
Касательно магии сложения/вычитания: обычно операции сложения/вычитания работают над объектами одного типа (если для их класса реализовано соответствующее поведение), а datetime/timedelta, это скорее исключение: нельзя сложить две даты — это лишено смысла, но можно изменить дату на некоторое кол-во фиксированных единиц (дней/часов/минут...) и нельзя увеличить дату на n месяцев/лет — кол-во дней плавает.
range() работает только с числами и «магию» не использует (за пределами операций над числами).
При этом её аргумент «шаг» — необязателен. А как тогда как ф-ция должна определять, чем инкрементить дату?
Можно написать всеядную «магическую» версию range() (я напишу «ленивый» вариант):
def range_(from_, to_, step):
while from_ < to_:
yield from_
from_ += step
Этот вариант работать будет для дат/времени (да и для списков, скажем), но потребует обязательного указания объекта-инкремента. Такое поведение неявно, ИМХО.
+1
просто у меня сложилось впечатление, что build-in функции в python, в основном, обобщенные: len(), min(), max()… — а специальные живут в отдельных модулях. кстати:
а range([start], stop, [step]) какой-то очень специфичный. не понятно, только — зачем? ведь, как вы указали, обобщенный вариант реализуется тривиально.
>>> from datetime import date, timedelta
>>> min(date(2012,01,01), date(2012, 01, 02))
datetime.date(2012, 1, 1)
>>> min(timedelta(days=3), timedelta(days=2))
datetime.timedelta(2)
а range([start], stop, [step]) какой-то очень специфичный. не понятно, только — зачем? ведь, как вы указали, обобщенный вариант реализуется тривиально.
0
len/min/max — это не функции даже, это обёртки над вызовом __len__/__lt__/__gt__ (ну может min/max посложнее чуток — там на входе итерируемый источник)
Т.е. len() возвращает длину объекта, который может её предоставить, min()/max() сравнивают сравнимые объекты, а их sort() сортирует, int()/str() приводит к целому/строке те объекты, которые сами могут представлять себя целым/строкой. Всё логично.
А range() именно такой, потому что имеет [start] и [step] — необязательные параметры, и такая сигнатура в общем случае не позволяет определить начальный элемент, и уж тем более шаг! Поэтому поддерживаемый типы аргументов ограничены числами, и начало/шаг имеют значения по-умолчанию. Ну и range() чаще всего используется в качестве источника индексов для прохода по индексируемому контейнеру, а тут как раз целые числа используются в качестве индекса, и начальный элемент контейнера обычно имеет индекс «0».
Если бы можно было ввести такие маг.методы:
__min__ — минимальный элемент данного типа
__max__ — максимальный элемент данного типа
__succ__(__pred__) — следующий(предыдущий) за текущим объектом элемент данного типа
можно было-бы реализовать range более всеядным — по-умолчанию отсчет шел бы от __min__, а инкремент происходил бы через __succ__. Всё это реализуемо, но встроенные типы данных — нерасширяемы, поэтому смысла особого нет. Проще под ситуацию написать свой my_range(), c датой и дельтами :)
P.S. Ну и в конце концов, в документации к range() указано, что он работает с числами — всё прозрачно.
Т.е. len() возвращает длину объекта, который может её предоставить, min()/max() сравнивают сравнимые объекты, а их sort() сортирует, int()/str() приводит к целому/строке те объекты, которые сами могут представлять себя целым/строкой. Всё логично.
А range() именно такой, потому что имеет [start] и [step] — необязательные параметры, и такая сигнатура в общем случае не позволяет определить начальный элемент, и уж тем более шаг! Поэтому поддерживаемый типы аргументов ограничены числами, и начало/шаг имеют значения по-умолчанию. Ну и range() чаще всего используется в качестве источника индексов для прохода по индексируемому контейнеру, а тут как раз целые числа используются в качестве индекса, и начальный элемент контейнера обычно имеет индекс «0».
Если бы можно было ввести такие маг.методы:
__min__ — минимальный элемент данного типа
__max__ — максимальный элемент данного типа
__succ__(__pred__) — следующий(предыдущий) за текущим объектом элемент данного типа
можно было-бы реализовать range более всеядным — по-умолчанию отсчет шел бы от __min__, а инкремент происходил бы через __succ__. Всё это реализуемо, но встроенные типы данных — нерасширяемы, поэтому смысла особого нет. Проще под ситуацию написать свой my_range(), c датой и дельтами :)
P.S. Ну и в конце концов, в документации к range() указано, что он работает с числами — всё прозрачно.
0
В целом, «магические методы» — это в первую очередь Методы, и уж во вторую — «магические». И как любые методы — должны быть определены для объекта где-то в его иерархии наследования: «a + b» это, всего навсего, «a.__add__(b)», только с некоторой долей синтаксического сахара, отсюда и вся «волшебность». И для datetime определен метод __add__, который может принимать объект timedelta в качестве аргумента.
А range() — обычная функция, реализованная с поддержкой чисел и ничего кроме. При этом ф-ция имеет проверку на вызов для других типов данных — и возбуждает исключение, причем вполне конкретное, с объяснением причины!
А range() — обычная функция, реализованная с поддержкой чисел и ничего кроме. При этом ф-ция имеет проверку на вызов для других типов данных — и возбуждает исключение, причем вполне конкретное, с объяснением причины!
0
Таких статей уже было миллион. Перевод очень плохой.
+1
Sign up to leave a comment.
Вещи, о которых следует помнить, программируя на Python