Как я могу сбалансировать «Pythonic» и «удобно» в этом случае? - PullRequest
5 голосов
/ 18 августа 2011

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

class Runner:
    def run(self):
        pass

run в общем случае должен возвращать docutils node, но потому, что наиболее далеко распространенный случай просттекст, вызывающий позволяет run вернуть строку, которая будет проверена с помощью type() и превращена в node.

Однако, как я понимаю, «Pythonic», это не «Pythonic».«потому что проверка type() чего-либо не позволяет ему« быть »типом,« действуя »как единое целое - т. е.« Pythonic »код должен использовать типизацию утки.*

но меня это не волнует, потому что оно помещает не очень интересный тип возврата прямо в имя;это отвлекает.

Есть какие-то идеи, которые я пропустил?Кроме того, есть ли проблемы, с которыми я мог бы столкнуться в будущем со своей «плохой» системой (она кажется мне более или менее безопасной)?

Ответы [ 5 ]

5 голосов
/ 18 августа 2011

Я думаю, что это немного обманчивый пример;есть то, что вы не заявили.Я предполагаю, что когда вы говорите, что у вас «есть интерфейс», вы имеете в виду, что у вас есть код, который принимает объект и вызывает его метод run.

Если вы не проверяете тип этого объекта перед вызовом его метода run, то вы используете простую и понятную типизацию утиной команды!(В этом случае, если у него есть метод run, то это Runner.) Пока вы не используете type или isinstance для объекта с методом run, тогда вы 'ты пифоник.

Вопрос о том, должны ли вы принимать простые строки или только объекты узлов, является немного другим вопросом.Строки и объекты node, вероятно, вообще не реализуют один и тот же интерфейс!Струны в основном не крякают как node, поэтому вам не нужно обращаться с ними как с одним.Это как слон, который приходит вместе, и если вы хотите, чтобы он крякал как утка, вы должны дать слону магнитофон и приучить слона использовать его первым.

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

Чтобы дать вам ответ, тогда на этом уровне, я думаю, наиболее питонично предположить, что run возвращает объект node.Для этого не нужно использовать isinstance или type.Просто представьте, что это объект node, и если программист, использующий ваш интерфейс, поймет это неправильно и увидит исключение, ему придется прочитать вашу строку документации, которая скажет им, что run должен передать объект node.

Тогда, если вы хотите, чтобы также принимал строки или вещи, которые крякают как строки, вы можете сделать это.И поскольку строки являются довольно примитивными типами, я бы сказал, что не следует использовать isinstance(obj, basestring) (но не type(obj) == str, потому что это отклоняет строки в юникоде и т. Д.).По сути, вы очень либеральны и добры к ленивым пользователям вашей программы;Вы уже делаете все возможное, принимая слонов, а также вещи, которые крякают, как утки.

(Более конкретно, я бы сказал, что это немного похоже на вызов iter для аргумента в начале функции, которую вы хотите принять как генераторы, так и последовательности.)

2 голосов
/ 18 августа 2011

Вам не обязательно иметь методы для обработки каждого типа, особенно если простая операция - это все, что произойдет.Обычный подход Pythonic заключается в следующем:

def run(self):
    try:
        ...assume it's a str   
    except TypeError:
        ...oops, not a str, we'll address that

Это соответствует стилю кодирования Проще просить прощения, чем разрешению (EAFP) , который обычно быстрее и проще.

1 голос
/ 18 августа 2011

Использование type() для определения типа переменной - это действительно плохая практика, так как это не позволит объекту наследовать от желаемого типа (str в вашем случае), лучше использовать isinstance():

if isinstance(my_var, str):
    my_code_here()

Кроме того, питонский способ сделать это, как вы уже упоминали, печатать на утке, почему бы вам просто не поместить код в блок try / except?Таким образом, исключение будет перехвачено, только если значение не действует , как ожидалось (если оно крякает и ходит как утка, это утка).

1 голос
/ 18 августа 2011

Проверьте Ошибки и исключения. Вы можете сделать что-то вроде этого:

def run(self,arg):
    try:
        return make_node(arg)
    except AlreadyNodeError:
        pass

Внутри вашей функции make_node вызовите AlreadyNodeError, если аргумент уже является узлом.

0 голосов
/ 18 августа 2011
class Node(object):
  def __new__(cls, contents):
    return contents if isinstance(contents, cls) else object.__new__(cls)
  def __init__(self, contents):
    # construct from string...

class Manager(object):
  def do_something(self, runner, *args):
    do_something_else(Node(runner(*args)))

Теперь не имеет значения, возвращает ли бегун нод или строку.

...