Как создать код в Python?
Я прихожу из Java и изучаю Python. До сих пор то, что я нашел очень классным, но очень трудно адаптироваться, заключается в том, что нет необходимости объявлять типы. Я понимаю, что каждая переменная является указателем на объект, но пока я не могу понять, как создать код.
Например, я пишу функцию, которая принимает массив 2D NumPy. Тогда в теле функции я вызываю различные методы этого массива (который является объектом array
в Numpy). Но затем в будущем предположим, что я хочу использовать эту функцию, к тому времени я мог бы полностью забыть, что я должен передать функции как тип. Что люди обычно делают? Они просто пишут для этого документацию? Потому что, если это так, то это требует большего набора текста и поставит вопрос о том, чтобы не объявлять тип.
Также предположим, что я хочу передать объект, похожий на массив в будущем. Обычно в Java реализуется интерфейс, а затем пусть оба класса реализуют методы. Затем в параметрах функции я определяю переменную типа интерфейса. Как решить эту проблему на Python или какие подходы могут быть использованы для создания одной и той же идеи?
Ответы
Ответ 1
Это очень здоровый вопрос.
Утиная печать
Первое, что нужно знать о python, - это концепция утиной печати:
Если он ходит, как утка, и крякает, как утка, тогда я называю это уткой
В отличие от Java, типы Python никогда не объявляются явно. Нет ограничений, ни во время компиляции, ни во время выполнения, в типе, который может принять объект.
То, что вы делаете, просто обрабатывает объекты так, как если бы они были идеального типа для ваших нужд. Вы не спрашиваете или не задаетесь вопросом о его типе. Если он реализует методы и атрибуты, которые вы хотите получить, то это. Это будет сделано.
def foo(duck):
duck.walk()
duck.quack()
Единственный контракт этой функции состоит в том, что duck
предоставляет walk()
и quack()
. Более утонченный пример:
def foo(sequence):
for item in sequence:
print item
Что такое sequence
? A list
? Числовое значение array
? A dict
? A generator
? Это не имеет значения. Если он повторяется (т.е. Он может использоваться в for ... in
), он выполняет свою задачу.
Тип подсказки
Конечно, никто не может жить в постоянном страхе перед объектами неправильного типа. Это рассматривается с помощью стиля кодирования, условных обозначений и хорошей документации. Например:
- Переменная с именем
count
должна содержать целое число
- Переменная
Foo
, начинающаяся с буквы верхнего регистра, должна содержать type
(class
)
- Аргумент
bar
, значение которого по умолчанию False
, также должен содержать bool
при переопределении
Обратите внимание, что к этим 3 примерам можно применить концепцию типирования утки:
-
count
может быть любым объектом, который реализует +
, -
и <
-
Foo
может быть любым вызываемым, возвращающим экземпляр объекта
-
bar
может быть любым объектом, который реализует __nonzero__
Другими словами, тип никогда не определяется явно, но всегда сильно намекает. Вернее, возможности объекта всегда намекают, а его точный тип не имеет значения.
Очень распространено использование объектов неизвестных типов. Большинство фреймворков выставляют типы, которые выглядят как списки и словари, но не являются.
Наконец, если вам действительно нужно знать, там есть документация. Вы найдете документацию python значительно превосходящую для Java. Это всегда стоит прочитать.
Ответ 2
Я просмотрел много кода на Python, написанных разработчиками Java и .Net, и я неоднократно видел несколько проблем, о которых я мог бы предупредить/сообщить:
Python не является Java
Не вставляйте все в класс:
Похоже, что даже самая простая функция завершается, завернувшись в класс, когда разработчики Java начинают писать Python. Python - это не Java. Не записывайте геттеры и сеттеры, для чего предназначен декоратор свойств.
У меня есть два предиката, прежде чем я буду рассматривать классы:
- Я выхожу замуж за состояние с функциональностью
- Я ожидаю, что у меня будет несколько экземпляров (в противном случае уровень модуля и функции в порядке!)
Не печатайте - проверяйте все
Python использует утиную печать. См. Модель данных. Его встроенное принуждение - ваш друг.
Не помещайте все в блок try-except
Исключайте только исключения, которые, как вы знаете, получите, используя исключения во всем мире для потока управления, являются дорогостоящими вычислениями и могут скрыть ошибки. Попробуйте использовать наиболее конкретное исключение, которое вы ожидаете получить. Это приводит к созданию более надежного кода в долгосрочной перспективе.
Изучите встроенные типы и методы, в частности:
Из модели данных
str
-
join
- просто сделайте
dir(str)
и изучите их все.
list
-
append
(добавьте элемент в конец списка)
-
extend
(расширьте список, добавив каждый элемент в итерируемый)
dict
-
get
(укажите значение по умолчанию, которое не позволяет вам захватывать ключевые слова!)
-
setdefault
(устанавливается по умолчанию или уже имеющееся значение!)
-
fromkeys
(постройте dict со значениями по умолчанию из итерируемого ключей!)
set
Наборы содержат уникальные (без репликации) хешируемые объекты (например, строки и числа). Мышление диаграммы Венна? Хотите узнать, есть ли набор строк в наборе других строк или какие перекрытия (или нет?)
-
union
-
intersection
-
difference
-
symmetric_difference
-
issubset
-
isdisjoint
И просто сделайте dir()
для каждого типа, с которым вы сталкиваетесь, чтобы увидеть методы и атрибуты в своем пространстве имен, а затем выполните help() для атрибута, чтобы узнать, что он делает!
Изучите встроенные функции и стандартную библиотеку:
Я поймал разработчиков, которые пишут свои собственные функции max и устанавливают объекты. Это немного неловко. Не позволяйте этому случиться с вами!
Важными модулями, которые должны быть в стандартной библиотеке, являются:
-
os
-
sys
-
collections
-
itertools
-
pprint
(я использую его все время)
-
logging
-
unittest
-
re
(регулярные выражения невероятно эффективны при разборе строк для большого количества случаев использования)
И просмотрите документы для краткого ознакомления с стандартной библиотекой, здесь Часть 1 и здесь Часть II. И вообще, сделайте снимок всех документов ранней целью.
Прочтите руководства по стилям:
Вы узнаете много о лучших практиках, просто прочитав свои руководства по стилю! Я рекомендую:
Кроме того, вы можете узнать отличный стиль по Googling по проблеме, которую вы изучаете, с фразой "лучшая практика", а затем выбрав соответствующие ответы Stackoverflow с наибольшим количеством upvotes!
Я желаю вам удачи на пути к изучению Python!
Ответ 3
Например, я пишу функцию, которая принимает массив 2D Numpy. Тогда в теле функции я вызываю различные методы этого массива (который является объектом массива в Numpy). Но тогда в будущем предположим, что я хочу использовать эту функцию, к тому времени я мог бы полностью забыть, что я должен передать функции как тип. Что люди обычно делают? Они просто пишут для этого документацию?
Вы пишете документацию и соответствующим образом называете функцию и переменные.
def func(two_d_array):
do stuff
Также предположим, что в будущем я хочу передать объект, похожий на массив, как правило, в Java, который реализует интерфейс, а затем пусть оба класса реализуют методы.
Вы можете это сделать. Создайте базовый класс и наследуйте его, чтобы несколько типов имели один и тот же интерфейс. Однако, довольно часто, это излишне, и вместо этого вы просто используете утиную печать. С утиным набором текста все, что имеет значение, - это то, что оцениваемый объект определяет правильные свойства и методы, необходимые для его использования в вашем коде.
Обратите внимание, что вы можете проверить типы в Python, но это обычно считается плохой практикой, поскольку это мешает вам использовать утиную печать и другие шаблоны кодирования, включенные системой динамического типа Python.
Ответ 4
Да, вы должны документировать, какие типы аргументов ожидаются вашими методами, и до вызывающего, чтобы передать правильный тип объекта. Внутри метода вы можете написать код для проверки типов каждого аргумента или просто предположить его правильный тип и полагаться на Python, чтобы автоматически генерировать исключение, если переданный объект не поддерживает методы, которые ваш код необходимо вызвать его.
Недостаток динамической типизации заключается в том, что компьютер не может выполнить такую же проверку правильности переднего плана, как вы отметили; есть большая нагрузка на программиста, чтобы убедиться, что все аргументы имеют правильный тип. Но преимущество в том, что у вас гораздо больше гибкости в том, какие типы могут быть переданы вашим методам:
- Вы можете написать метод, который поддерживает несколько различных типов объектов для конкретного аргумента, без необходимости перегрузки и дублированного кода.
- Иногда метод действительно не заботится о точном типе объекта, если он поддерживает определенный метод или операцию - скажем, индексирование с квадратными скобками, которое работает с строками, массивами и множеством других вещей. В Java вам нужно создать интерфейс и написать классы-оболочки для адаптации различных ранее существующих типов к этому интерфейсу. В Python вам не нужно ничего делать.
Ответ 5
Вы можете использовать assert
для проверки соответствия условий:
In [218]: def foo(arg):
...: assert type(arg) is np.ndarray and np.rank(arg)==2, \
...: 'the argument must be a 2D numpy array'
...: print 'good arg'
In [219]: foo(np.arange(4).reshape((2,2)))
good arg
In [220]: foo(np.arange(4))
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-220-c0ee6e33c83d> in <module>()
----> 1 foo(np.arange(4))
<ipython-input-218-63565789690d> in foo(arg)
1 def foo(arg):
2 assert type(arg) is np.ndarray and np.rank(arg)==2, \
----> 3 'the argument must be a 2D numpy array'
4 print 'good arg'
AssertionError: the argument must be a 2D numpy array
Всегда лучше документировать то, что вы написали полностью, как упоминал @ChinmayKanchi.
Ответ 6
Вот несколько указателей, которые могут помочь вам сделать ваш подход более "Pythonic".
PEPs
В общем, я рекомендую по крайней мере просматривать PEPs. Мне очень помогли Python.
Указатели
Поскольку вы упомянули указатели на слова, Python не использует указатели на объекты в том смысле, что C использует указатели. Я не уверен в отношении к Java. Python использует имена, прикрепленные к объектам. Это тонкое, но важное отличие, которое может вызвать у вас проблемы, если вы ожидаете поведения указателя с аналогичным положением.
Duck Typing
Как вы сказали, да, если вы ожидаете определенный тип ввода, вы помещаете его в docstring.
Как писал zhangxaochen, вы можете использовать assert для выполнения ввода в реальном времени своих аргументов, но это не совсем путь python, если вы делаете это все время без особых причин. Как уже упоминалось, лучше проверить и поднять TypeError, если вам нужно это сделать. Python предпочитает вместо утиного набирать текст - если вы посылаете мне что-то такое, что quacks, как многомерный 2D-массив, тогда это прекрасно.