2016-09-14 2 views
0

У меня есть справочная таблица, как это:Использует ли тип в качестве словарных ключей, считающихся хорошей практикой в ​​Python?

lookup = { 
    "<class 'Minority.mixin'>": report_minority, 
    "<class 'Majority.mixin'>": report_majority, 
} 

def report(o): 
    h = lookup[str(type(o))] 
    h() 

Это выглядит неудобно мне как key будет непрочно связан к тому, как type() возвращается и представляет в string в class. Если в один прекрасный день Python изменит свой путь до class типов в string, все эти коды сломаны. Поэтому я хочу получить советы от профессионалов, такой тип ключей считается хорошим или плохим? Благодарю.

ответ

2

Вопрос будет более Почему вы это делаете в первую очередь? Какое преимущество вы думаю используя струны есть?

объекты класса hashable, так что вы можете использовать Minority.mixin и Majority.mixinнепосредственно в качестве ключей. И когда вы это сделаете, вы гарантируете, что ключ всегда тот же самый объект, если ваши классы являются глобальными в своих соответствующих модулях, что делает их одноклассниками вашей программы. Более того, нет никаких шансов на случайную путаницу, когда вы позже реорганизуете свой код для переименования модулей, и вы получите разные типы с таким точным результатом repr().

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

(И даже если вы должны были генерировать классы с заводской функцией, использование базового класса и isinstance, вероятно, будет предпочтительным проверять или извлекать базовый класс из MRO).

Так, для USECASE, придерживайтесь:

lookup = { 
    Minority.mixin: report_minority, 
    Majority.mixin: report_majority, 
} 

def report(o): 
    h = lookup[type(o)]) 
    h() 

Далее, если вы убедитесь, что report_minority и report_majority являются функции (а не методы, например), вы можете использовать functools.singledispatch() и обойтись без вашего отображение в целом:

from functools import singledispatch 

@singledispatch 
def report(o): 
    raise ValueError('Unhandled type {}'.format(type(o))) 

@report.register(Minority.mixin) 
def report_minority(o): 
    # handle a Minority instance 

@report.register(Majority.mixin) 
def report_majority(o): 
    # handle a Majority instance 

Обратите внимание, что это не будет работать с методами, как и методы должны принять несколько аргументов для отправки на работу, так как они всегда принимают self.

Одиночные диспетчерские ручки подклассы гораздо лучше, в отличие от вашего str - на основе картографирования или даже прямого сопоставления классов.

+0

Неважно, как долго я пишу в Python, я все еще узнаю что-то из каждого вашего ответа Martijn. –

+0

Мне так повезло, что Мартинн ответил на мой вопрос. На самом деле, как его (или, может быть, она, мы никогда не встречались) стиль ручной работы, помогает много! –

+0

@ Martijn Pieters ♦ Не могли бы вы немного объяснить слово «одиночные игры» во втором абзаце? Это относится к одноэлементному шаблону? Мне кажется, что в моем примере не существует такой модели, не так ли? Благодарю. –

0

Посмотрите на этот сценарий, я не думаю, что это прекрасно, но это работает, и я думаю, что это более элегантно, чем ваше решение:

#!/usr/bin/env python3 

class Person: 
    pass 

def display_person(): 
    print("Person !") 

class Car: 
    pass 

def display_car(): 
    print("Car !") 

lookup = { 
    Person: display_person, 
    Car: display_car 
} 


def report(o): 
    lookup[o.__class__]() 


report(Person()) 
report(Car()) 

Edit: модификация для Python3

+0

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

+0

@ Dorian Также производительность мудрая, итерация намного дороже, чем поиск хэша, не так ли? –

+0

Я думаю, что это более элегантно, потому что 'isistance' - лучший способ проверить тип объекта, чем использовать' type'. Но вы правы, мне тоже не нравится этот цикл, это может быть дороже, если вы укажете только два элемента, это не очень важно, но может быть, если вы наберете больше. – Dorian

1

Просто удалите str из поиска и из словаря. Таким образом,

lookup = { 
    Minority.mixin : report_minority, 
    Majority.mixin : report_majority, 
} 

def report(o): 
    h = lookup[type(o)] 
    h() 

Это исправляет вашу непосредственную озабоченность и является довольно разумным кодом. Однако, похоже, что отправка по типу - это именно то, для чего OO. Итак, почему бы не сделать эти различные методы функций отчета на объекте o, полученном из его типа? Тогда вы могли бы просто написать:

o.report() 

и правильный вариант был получен из класса.

+0

Спасибо также за то, что вы указали, что направление полиморфизма. К сожалению, моя реализация включает в себя множество неизменных классов Python, а также включает в себя запись в базу данных, поэтому подклассификация этих классов и добавление отдельного отчета() кажутся менее элегантными, чем этот маршрут диспетчеризации. Но я не был так уверен, пока не читал сообщения здесь. –

Смежные вопросы