Найти - Пользователи
Полная версия: как создать экземпляр класса, если имя класса задано строкой
Начало » Python для новичков » как создать экземпляр класса, если имя класса задано строкой
1
trympyrym
Необходимо в зависимости от условий создать экземпляр одного из N классов

сейчас реализация такая:

если (условие 1):
Экземпляр = класс1()
если (условие 2):
Экземпляр = класс2()


если (условие N):
Экземпляр = классN()

Неудобно это очень. Подскажите, есть ли команда, принимающая строку - название класса и создающая его экземпляр?

ЗЫ. все классы имеют одинаковый интерфейс, но очень сильно друг от друга отличаются, и при этом довольно тяжелые, поэтому лепить монстра, который ведет себя как класс K при инициализации с параметром K не представляется возможным.
FishHook
class A(object):
	def foo(self):
		print "A"
class B(object):
	def foo(self):
		print "B"
cls_name = "A"
instance = locals()[cls_name]()
instance.foo()
trympyrym
Это именно то, что надо. Спасибо.
sergeek
не понятно каким образом эта команда упростит задачу. Классы такие же объекты как и строки
FishHook
sergeek
не понятно каким образом эта команда упростит задачу. Классы такие же объекты как и строки

Например, есть у нас роли пользователя, которые мы храним в профиле.
Типа такого
role = IntegerField(choices=[(1, "Moderator"), (2, "Member"), (3, "Admin")]
И три класса некой формы
ModeratorForm(forms.ModelForm):
....
AdminForm(forms.ModelForm):
....
def get_form_by_user(user, *args, **kwargs):
    form_class_name = user.get_role_display() + "Form"
    form = locals()[form_class_name](*args, **kwargs)
    return form
sergeek
FishHook
да, это видимо практично (хотя мне все равно кажется это следствием плохой архитектуры самой библиотеки)
но тсу ведь не нравилась простыня из условий
trympyrym
если (условие 1):
Экземпляр = класс1()
если (условие 2):
Экземпляр = класс2()
возможно все же лучше было решить это каким-нибудь хитрым полиморфизмом или словарем {лямбда : класс}


FishHook
Что-то типа этого, взято из Википедии
# -*- coding: utf-8 -*-
 
 
class BaseVehicle(object):
    def get_message(self):
        raise NotImplementedError()
 
 
class Airplane(BaseVehicle):
    """Самолет"""
    def __init__(self):
        self.message = 'I am airplane'
 
    def get_message(self):
        return self.message
 
 
class Car(BaseVehicle):
    """Машина"""
    def __init__(self):
        self.message = 'I am car'
 
    def get_message(self):
        return self.message
 
 
class VehicleFactory(object):
    """Фабрика средств передвижения"""
    objects = {
        'car': Car,
        'airplane': Airplane,
        'default': Airplane,
    }
 
    def get_vehicle(self, name='default'):
        return self.objects[name]()
 
 
factory = VehicleFactory()
car = factory.get_vehicle('car')
airplane = factory.get_vehicle('airplane')
 
print car.get_message()  # I am car
print airplane.get_message()  # I am airplane

Я бы с Вами согласился, если бы мы имели дело со строготипизированным языком. Минус такого решения паттерна Абстрактная фабрикаи в том, что мы так или иначе должны регистрировать классы, как здесь в objects. А можем сделать проще, если примем некоторое соглашение об именовании классов. Тогда добавление нового класса в модуль автоматически делает его возможным результатом фабрики. Это менее академично, зато надёжно и практично.
sergeek
FishHook
Это менее академично, зато надёжно и практично
да, именно это я и пытался выразить
FishHook
Минус такого решения паттерна Абстрактная фабрикаи в том, что мы так или иначе должны регистрировать классы, как здесь в objects
их не обязательно регистрировать, можно, например добавлять (или даже генерировать из имени класса) атрибут
class Car(BaseVehicle):
    """Машина"""
    name = 'car'
    def __init__(self):
        self.message = 'I am car'
 
    def get_message(self):
        return self.message
и пройтись по всем потомкам BaseVehicle в поисках нужного.
FishHook
sergeek
>>и пройтись по всем потомкам BaseVehicle в поисках нужного.
>>их не обязательно регистрировать, можно, например добавлять (или даже генерировать из имени класса) атрибут
Отлично! Но мы, (если уж мы решили пойти правильным путем ) должны обеспечить уникальность атрибута name у потомков, иначе мы не получим ни ошибки компиляции ни ошибки времени выполнения, и вообще даже можем не предполагать, что программа работает неправильно, если два класса будут иметь одинаковый name.
Экстраполируя, мы придем к Hello Word с применением тридцати паттернов, компонентным архитектурам, внедрению зависимостей и прочим страшным модным словам, хотя можно всё сделать проще.
sergeek
FishHook
Отлично! Но мы, (если уж мы решили пойти правильным путем ) должны обеспечить уникальность атрибута name у потомков, иначе мы не получим ни ошибки компиляции ни ошибки времени выполнения, и вообще даже можем не предполагать, что программа работает неправильно, если два класса будут иметь одинаковый name.
напишем метакласс-валидатор требующий уникальный атрибут для каждого неабстрактного потомка
(3 питон)
import abc
 
class Vehicle_validator(type):
    name_set = set()
    
    def __new__(cls, clsname, bases, attrs):
        is_abstract = lambda a : hasattr(a, '__isabstractmethod__')
        
        if not any(map(is_abstract, attrs.values())):            
            veh_name = attrs.get('name')
            assert veh_name, "vehicle name for {} is not provided!!".format(clsname)
            assert veh_name not in Vehicle_validator.name_set, 'nonunique vehicle name in {}!!1'.format(clsname)
            Vehicle_validator.name_set.add(veh_name)
                    
        return super(Vehicle_validator, cls).__new__(cls, clsname, bases, attrs)
        
 
class Vehicle_meta(Vehicle_validator, abc.ABCMeta):
    ...
 
class BaseVehicle(metaclass=Vehicle_meta):
 
    @abc.abstractmethod
    def get_message(self):
        ...
 
class Car(BaseVehicle):
    """Машина"""
    name = 'car'
    def __init__(self):
        self.message = 'I am car'
 
    def get_message(self):
        return self.message
class Airplane(BaseVehicle):
    #name = 'car'
    """Самолет"""
    def __init__(self):
        self.message = 'I am airplane'
 
    def get_message(self):
        return self.message
 
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB