Каков канонический способ обработки различных типов в Python? - PullRequest
6 голосов
/ 29 октября 2010

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

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

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

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

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

    cmdline = 'magicprogram ' + cmd_type + ' ' + val
    Popen(cmdline, ... blah blah)
    ...

, который прекрасно работает, но я просто хотел узнать, есть ли какая-то техника, о которой я не знаю.

Ответы [ 4 ]

5 голосов
/ 29 октября 2010

Вы можете использовать Двойная отправка или Мультиметоды .

3 голосов
/ 30 октября 2010

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

Не удивительно, чтобы сделать то, что вы делаете, более объектно-ориентированным, вам нужно ввести в него некоторые объекты (и соответствующие классы). Создание каждого значения экземпляром класса позволит - фактически, фактически - прекратит проверку его типа. Модификации вашего примера кода ниже показывают очень простой способ, которым это можно было бы сделать:

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(), чтобы использовать для создания, манипулирования и извлечения информации из них.

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

3 голосов
/ 29 октября 2010
    But I noticed how people also raised objections, 
that checking for types betrays a "not object oriented" design

На самом деле это называется Стиль печатания утки («Если это похоже на утку и крякает как утка, это должна быть утка».), И это язык Python, который рекомендует использовать этот стиль программирования .

и с уткой набрав что-нибудь, позвоните 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

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

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

1 голос
/ 29 октября 2010

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

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

Val = "a string"
myfunc = myfuncStringVersion

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

...