Каков канонический способ обработки различных типов в Python?


У меня есть функция, в которой мне нужно генерировать различные выходные строки для другой программы, которую я вызываю, в зависимости от того, какой тип она хочет.

В принципе, вызываемая программа нуждается в аргументе командной строки, сообщающем ей, с каким типом она была вызвана.

К счастью, я нашелЭтот ответ на так далее, Как проверить переменную для типа. Но я заметил, как люди также возражали, что проверка типов выдает "не объектно-ориентированный" дизайн. Итак, есть ли какой-то другой способ, предполагаемый более "более объектно-ориентированный" способ обработки этого без явной проверки типа?

Код, который у меня сейчас есть, звучит примерно так:

def myfunc(val):
    cmd_type = 'i'
    if instance(val, str):
        cmd_type = 's'

    cmdline = 'magicprogram ' + cmd_type + ' ' + val
    Popen(cmdline, ... blah blah)
    ...
Это работает просто отлично, но я просто хотел узнать, есть ли какая-то техника, о которой я не знаю.
4 6

4 ответа:

Я не думаю, что двойная диспетчеризация или Мультиметоды особенно важны и не имеют большого отношения к возражениям, которые люди имели к этому другому ответу.

Неудивительно, что для того, чтобы сделать то, что вы делаете, более объектно-ориентированным, вам нужно будет ввести в него некоторые объекты (и соответствующие классы). Превращение каждого значения в экземпляр класса позволит-фактически, принудительно-прекратить проверку его типа. Изменения в вашем примере кода ниже показывают очень это можно было сделать простым способом:
class Value(object):
    """ Generic container of values. """
    def __init__(self, type_, val):
        self.type = type_   # using 'type_' to avoid hiding built-in
        self.val = val

def myfunc(val):
    # Look ma, no type-checking!
    cmdline = 'magicprogram {obj.type} {obj.val}'.format(obj=val)
    print 'Popen({!r}, ... blah blah)'.format(cmdline)
    # ...

val1 = Value('i', 42)
val2 = Value('s', 'foobar')

myfunc(val1)  # Popen('magicprogram i 42', ... blah blah)
myfunc(val2)  # Popen('magicprogram s foobar', ... blah blah)

Было бы еще более объектно-ориентированным, если бы в классе Value были методы для косвенного доступа к его атрибутам, но просто выполнение вышеизложенного избавляет от печально известной проверки типов. Более объектно-ориентированный дизайн, вероятно, будет иметь различные подклассы для каждого вида Value, которые все имеют общий набор методов для клиентов, таких как myfunc(), чтобы использовать их для создания, манипулирования и извлечения информации из них.

Еще одно преимущество использования objects - это то, что вам не нужно изменять myfunc(), Если/когда вы добавляете поддержку нового типа "Value" в ваше приложение-если ваша абстракция сущности "Value" является хорошей, то есть.

Можно использовать Double Dispatch или Multimethods.

    But I noticed how people also raised objections, 
that checking for types betrays a "not object oriented" design

На самом деле это называется Duck typing style ("если он выглядит как утка и крякает как утка, это должна быть утка."), и именно язык python рекомендует использовать этот стиль программирования .

И с уткой набрав приходит что-то call EAFP (легче попросить прощения, чем разрешения)

    presumable more "more object oriented" way of handling this without 
   explicitly checking for type?

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

def myfunc(val):
    cmd_type = 'i'

    # forget about passing type to your magicprogram
    cmdline = 'magicprogram  %s ' % val 
    Popen(cmdline, ... blah blah)

И в вашей магической программе (я не знаю если это ваш сценарий или ...), и потому что во всех случаях ваша программа получит строку, так что просто попробуйте преобразовать ее в любой ваш сценарий принять;

from optparse import OptionParser

# ....

if __name__ == '__main__':

    parser = OptionParser(usage="blah blah")

    # ...
    (options, args) = parser.parse_args()

    # Here you apply the EAFP with all type accepted.
    try:
        # call the function that will deal with if arg is string
        # remember duck typing.
    except ... :
        # You can continue here

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

Надеюсь, это прояснит для вас ситуацию.

Это больше инженерия в большом вопросе, чем как спроектировать одну маленькую функцию. Есть много различных способов сделать это, но они более или менее сводятся к одному и тому же общему мыслительному процессу. Там, где тип val известен, он должен указать, как он должен быть переведен в командную строку arg. Если бы это был я, я бы, вероятно, сделал val классом, у которого есть функция командной строки To, которая делает правильные вещи. Вы также можете назначить функцию myfunc определенного типа переменная, то вызовите это, когда вам нужно.

Edit: чтобы объяснить последнюю версию что-то вроде

Val = "a string"
myfunc = myfuncStringVersion

Более или менее делает то же самое, что вы сделали бы с переносом val в класс, только разбитый на значение и функцию, так как вы, возможно, не захотите переносить val в класс.