главные советы для тех, кто переходит со статического языка (java/c#) на динамический язык, такой как python

Каковы главные ошибки для тех, кто переходит со статического языка (java/c#) на динамический язык, такой как python?

Кажется, здорово, как это можно сделать, но переименование метода или добавление/удаление параметров кажется таким рискованным!

Является ли единственным решением написать тесты для каждого метода?


person Blankman    schedule 13.09.2010    source источник


Ответы (3)


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

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

keys = 'foo bar foobar' # Imagine this coming in as an argument
keys = keys.split() # Now the semantically chose name for the argument can be 
                    # reused As the semantically chosen name for a local variable

не стесняйтесь относиться к функциям как к обычным значениям: так оно и есть. Возьмем следующий парсер. Предположим, что мы хотим обрабатывать все теги заголовков одинаково, а теги ul — как теги ol.

class Parser(HTMLParser):
    def __init__(self, html):
        self.feed(html)

    def handle_starttag(self, tag, attrs):
        parse_method = 'parse_' + tag    
        if hasattr(self, parse_method):  
            getattr(self, parse_method)(attrs)


    def parse_list(self, attrs):
        # generic code

    def parse_header(self, attrs):
       # more generic code

    parse_h1 = parse_h2 = parse_h3 = parse_h4 = parse_h5 = parse_h6 = parse_header
    parse_ol = parse_ul = parse_list

Это можно сделать, используя менее общий код в методе handle_starttag на таком языке, как java, отслеживая, какие теги сопоставляются с одним и тем же методом, но затем, если вы решите, что хотите обрабатывать теги div, вы должны добавить это в диспетчеризацию. логика. Здесь вы просто добавляете метод parse_div, и все готово.

Не проверяйте тип! Утиный тип!

def funtion(arg):
    if hasattr(arg, 'attr1') and hasattr(arg, 'attr2'):
         foo(arg):
    else:
         raise TypeError("arg must have 'attr1' and 'attr2'")

в отличие от isinstance(arg, Foo). Это позволяет передать любой объект с attr1 и attr2. Это позволяет вам, например, передать класс трассировки, обернутый вокруг объекта, для целей отладки. Вам нужно будет изменить класс, чтобы сделать это в Java AFAIK.

Как указывает THC4k, другим (более питоническим) способом сделать это является EAPF идиома. Мне это не нравится, потому что я люблю ловить ошибки как можно раньше. Это более эффективно, если вы ожидаете, что код будет редко давать сбои. Не говорите никому, что мне это не нравится, хотя они перестанут думать, что я умею писать на питоне. Вот пример, любезно предоставленный THC4k.

try: 
    foo(arg): 
except (AttributeError, TypeError): 
    raise InvalidArgumentError(foo, arg)

Вопрос в том, должны ли мы ловить AttributeError и TypeError или просто позволить им распространяться туда, где они знают, как с ними обращаться, но это всего лишь пример, поэтому мы позволим ему летать.

person aaronasterling    schedule 13.09.2010
comment
@THC4k. Как вы говорите, что это незнакомец? Если type(Foo) не является подклассом type, переопределяющим атрибут __instancecheck__, isinstance зависит от местоположения в иерархии классов. Способ, который я представил, лишен этого недостатка. LBYL против EAFP — еще одна проблема. Я забыл включить другой стиль. - person aaronasterling; 14.09.2010
comment
if hasattr(arg, 'attr1') and hasattr(arg, 'attr2') — это проверка типов. Он проверяет, принадлежит ли arg к (неявному) классу типов объектов, имеющих атрибуты attr1 и attr2. Это является более общим, чем использование isinstance, которое проверяет только один тип, а не весь класс типов. Но эти проверки часто не нужны: просто сразу выполните foo(arg), посмотрите, что произойдет, и перехватите исключения там, где это имеет смысл. Позвольте Python сделать все проверки типов за вас — если foo(arg) не имеет смысла, в конце концов он выдаст исключение. - person Jochen Ritzel; 14.09.2010
comment
@aaronasterling извините, да, плохая формулировка, я переписывал комментарий. - person Jochen Ritzel; 14.09.2010
comment
@aaronasterling: Я бы скорее сказал: try: .. except TypeError: .. принадлежит где-то в foo, вокруг той самой строки, которая использует arg таким образом, который зависит от некоторых свойств arg. except должен либо решить проблему с аргументом, либо вызвать пользовательское исключение (например, YouMessedUpException). - person Jochen Ritzel; 14.09.2010
comment
@THC4k. да, хороший звонок. мое сердце не было в этом. Я обновлю снова. - person aaronasterling; 14.09.2010
comment
Или например: try: foo(arg): except (AttributeError, TypeError): raise InvalidArgumentError(foo, arg) - person Jochen Ritzel; 14.09.2010

«Единственное решение — писать тесты для каждого метода?»

Вы хотите сказать, что не написали тесты для каждого метода в Java?

Если вы написали тесты для каждого метода на Java, то ничего не изменится, не так ли?

переименование метода кажется таким рискованным!

Правильный. Не делай этого.

добавление/удаление параметров кажется таким рискованным!

Какая? Вы говорите о необязательных параметрах? Если это так, то наличие нескольких перегруженных имен в Java кажется рискованным и запутанным. Наличие необязательных параметров кажется проще.


Если вы поищите на SO наиболее распространенные вопросы по Python, вы обнаружите, что некоторые вопросы являются хроническими.

  • Как обновить файл PYTHONPATH.

  • Почему некоторые случайные вычисления с плавающей запятой не совпадают с математической абстракцией.

  • Использование Python 3 и ввод кода из руководства по Python 2.

  • Почему в Python нет суперсложных объявлений protected, private и public.

  • Почему Python не имеет типа enum.

Хроническая проблема №1, по-видимому, заключается в использовании изменяемых объектов в качестве значений по умолчанию для функции. Просто избегайте этого.

person S.Lott    schedule 13.09.2010
comment
Мне, например, редко приходилось возиться с PYTHONPATH. И нет 3 - это независимая от языка проблема с поплавками, каждый (независимо от того, Java или C# или... или даже Scheme) достойный программист знает об этом. В противном случае согласен. - person ; 13.09.2010
comment
@delnan: Вы можете не связываться с этим, но на эту тему есть удивительное количество ТАКИХ вопросов. Кроме того, проблема с плавающей запятой должна быть хорошо известна. Тем не менее... Удивительно... SO заполнен 2,1 + 3,2 != 5,3 вопросами. - person S.Lott; 13.09.2010
comment
Итак, некоторые люди не получают путей импорта поиска и чисел с плавающей запятой — но это потому, что они взяты из статического языка? Скорее всего, нет - плавающие точки работают одинаково на всех языках, и, по крайней мере, в java есть что-то похожее на PYTHONPATH, CLASSPATH (поправьте меня, если я ошибаюсь, я не специалист по Java). - person ; 13.09.2010
comment
@delnan: Без понятия. Я просто предоставляю некоторую обратную связь, основанную на прочтении МНОЖЕСТВА вопросов по Python. Я не знаю, почему у людей возникают эти вопросы. Но они делают. - person S.Lott; 14.09.2010

Некоторые вещи, которые поразили меня, когда я впервые попробовал Python (в основном из-за опыта работы с Java):

  1. Напишите код Pythonic. Используйте идиомы, рекомендованные для Python, а не старый способ Java/C. Это больше, чем просто косметический или догматический вопрос. Pythonic-код на практике на самом деле намного быстрее, чем C-подобный код практически всегда. На самом деле, ИМХО, многие из понятий «Python медленный», витающие вокруг, связаны с тем, что неопытные кодеры пытались кодировать Java/C в Python и в конечном итоге получили большой удар по производительности и пришли к выводу, что Python ужасно медленно. Используйте списки и map/filter/reduce, когда это возможно.

  2. Усвойте идею о том, что функции на самом деле являются объектами. Передавайте их как обратные вызовы, заставляйте функции возвращать функции, узнавайте о замыканиях и т. д.

  3. Есть много классных и почти волшебных вещей, которые вы можете делать в Python, например, переименовывать методы, как вы упомянули. Эти вещи отлично подходят для демонстрации возможностей Python, но на самом деле они не нужны, если они вам не нужны. Действительно, как заметил С. Лотт, лучше избегать вещей, которые кажутся рискованными.

person MAK    schedule 13.09.2010
comment
Python работает медленнее. На порядок медленнее. Но это не имеет значения. Отчасти потому, что идиоматический код не настолько медленный, отчасти потому, что мы не используем трассировщики лучей в Python. Кроме того, классные вещи, которые делают язык таким медленным, также полностью компенсируют потерю производительности. - person ; 14.09.2010
comment
@дельнан. Я видел, как itertools превзошли чистый код C примерно на 20 секунд в восьмидесятиминутной задаче;) - person aaronasterling; 14.09.2010
comment
@aaronasterling: Разве itertools не является библиотекой C? (Я могу ошибаться) - person Daenyth; 14.09.2010
comment
@Дэнит. Это библиотека C AKAIK, но это часть сути: идиоматический python выталкивает вещи на C, используя как можно больше понятий и модулей, таких как itertools. - person aaronasterling; 14.09.2010
comment
@delnan: Я согласен, даже самый оптимизированный Python в целом медленнее по сравнению с C. Но на самом деле он не такой ужасно медленный, как некоторые люди считают. Я видел, что Python намного быстрее, чем Java, в очень ресурсоемких задачах. Дело в том, что написание Java/C на Python приводит к гораздо более медленному коду. - person MAK; 14.09.2010
comment
@МАК: +1. И написание любого дурацкого-старого-плохого-алгоритма на Python будет медленнее, чем написание того же тупого-старого-плохого-алгоритма на C. Оба варианта плохи, но C позволяет замаскировать недостатки с помощью оптимизации компилятора. - person S.Lott; 14.09.2010