Есть ли способ получить атрибуты экземпляра класса без создания экземпляра класса?

Вот ссылка на мой первый вопрос: Создание экземпляра класса из словаря?
Итак, я пытаюсь создать экземпляр класса из словаря, который содержит ключи, которых нет у класса. Например:

class MyClass(object):
    def __init__(self, value1, value2):
        self.attr1 = value1
        self.attr2 = value2

dict = {'attr1': 'value1', 'attr2': 'value2', 'redundant_key': 'value3'}

Перед созданием экземпляра класса я должен удалить этот redundant_key из dict

dict.pop('redundant_key', None)
new_instance = MyClass(**dict)

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

new_dict = {key: old_dict[key] for key in allowed_keys}

Итак, вот код:

class MyClass(object):
    def __init__(self, value1, value2):
        self.attr1 = value1
        self.attr2 = value2

dict = {'attr1': 'value1', 'attr2': 'value2', 'redundant_key': 'value3'}
new_instance = MyClass(**{key: dict[key] for key in allowed_keys})

Все, что мне сейчас нужно, это получить allowed_keys. Итак, вопрос: есть ли способ получить атрибуты экземпляра класса без создания экземпляра класса?


person Demyanov    schedule 08.05.2015    source источник
comment
во-первых, это невозможно, во-вторых, вы хотите получить аргументы функции, а не атрибуты экземпляра, что возможно путем проверки.   -  person Daniel    schedule 08.05.2015
comment
Это технически невозможно, атрибуты могут динамически добавляться и удаляться в экземплярах, поэтому нет смысла иметь их без фактического экземпляра. Я не уверен, зачем вам это нужно, где определяется allowed_keys?   -  person luk32    schedule 08.05.2015
comment
@Daniel Все, что мне нужно, это получить attr1 и attr2 в allowed_keys, когда attr1, attr2 являются атрибутами экземпляра класса.   -  person Demyanov    schedule 08.05.2015
comment
@ luk32 Я могу определить allow_keys для себя, но это около 10 диктов с 10-20 разными ключами, поэтому я подумал о том, как автоматически заполнять Allow_Keys. Мне действительно не нужны атрибуты экземпляра класса, мне нужны атрибуты, которые создаются во время метода __init__   -  person Demyanov    schedule 08.05.2015
comment
Глядя на ваш вариант использования, похоже, что вам нужны не атрибуты, которые создаст __init__, а его подпись аргумента: аргументы, которые он принимает. Это другая проблема, и у нее есть решение :-)   -  person alexis    schedule 08.05.2015
comment
@alexis: это то, что я сказал 17 минут назад. Но ОП не слушает :-(   -  person Daniel    schedule 08.05.2015
comment
Вы имеете в виду ненужное, а не лишнее.   -  person chepner    schedule 08.05.2015
comment
@chepner Да, извините за мой плохой английский   -  person Demyanov    schedule 08.05.2015


Ответы (4)


Если вы настаиваете на использовании слишком общего словаря для инициализации вашего объекта, просто определите __init__ принимать, но игнорировать дополнительные ключи.

class MyClass(object):
    def __init__(self, attr1, attr2, **kwargs):
        self.attr1 = attr1
        self.attr2 = attr2

d = {'attr1': 'value1', 'attr2': 'value2', 'extra_key': 'value3'}
new_instance = MyClass(**d)

Если вы не можете изменить __init__ (как кажется случае, если он наследуется от декларативной базы SQLAlchemy), добавьте альтернативный конструктор, чтобы принимать все аргументы ключевого слова, но выбирать те, которые вам нужны.

class MyClass(Base):
    @classmethod
    def from_dict(cls, **kwargs):
        # Let any KeyErrors propagate back to the caller.
        # They're the one who forgot to make sure **kwargs
        # contained the right keys.
        value1 = kwargs['attr1']
        value2 = kwargs['attr2']
        return cls(value1, value2)

new_instance = MyClass.from_dict(**d)
person chepner    schedule 08.05.2015
comment
Вы нарушили одно из самых основных правил программирования на Python, назвав переменную так же, как встроенную... - person martineau; 12.06.2017
comment
Нет, я скопировал исходный код пользователя и не стал его менять. - person chepner; 12.06.2017

Отказ от ответственности: это отвечает на вопрос, о чем спрашивала OP (получение атрибутов экземпляра), а не о том, что им нужно. Кажется, это список параметров конструктора.

Вы не можете делать то, что хотите. В python атрибуты добавляются к экземпляру класса динамически. Два экземпляра одного и того же класса могут иметь разные атрибуты. Ну... если быть точным, есть вещи, называемые атрибутами экземпляра и атрибутами класса.

Атрибуты экземпляра связаны с экземпляром класса. Атрибуты класса связаны с его определением, т.е. если вы написали (MyClass.foo)

Если вы посмотрите на пример, атрибуты добавляются во время __init__ на self, поэтому они являются атрибутами экземпляра.

Что вы можете сделать, так это создать действительный экземпляр и проверить его (см. ниже пример), предоставить статический список allowed_keys (например, в качестве атрибута класса) или каким-то образом потребовать allowed_keys в качестве параметра конструктора. . Все это своего рода обходные пути, поскольку то, что вам действительно нужно, невозможно. У них есть свои плюсы и минусы.

пример:

class MyClass(object):
    def __init__(self, value1, value2):
        self.attr1 = value1
        self.attr2 = value2

instance = MyClass(1,2)                        # create a valid object
instance_attributes = instance.__dict__.keys() # inspect it
print(instance_attributes)
person luk32    schedule 08.05.2015
comment
Мне действительно не нужны атрибуты экземпляра класса, мне нужны атрибуты, которые создаются во время метода __init__. Есть ли способ получить атрибуты, которые добавляются во время метода __init__? - person Demyanov; 08.05.2015
comment
Случай, когда присутствует понятие атрибутов класса, — это когда класс имеет __slots__ вместо словаря атрибутов. - person alexis; 08.05.2015
comment
Это решение может иметь побочные эффекты, не будет работать в каждом случае, и вам уже нужно знать параметры __init__ для создания экземпляра класса. - person Daniel; 08.05.2015
comment
Я не говорю, что это хорошая идея. Также вы знаете количество параметров, а не имена атрибутов, которые кажутся необходимыми. - person luk32; 08.05.2015
comment
@ Даниэль, кажется, я понял тебя. Я написал небольшой отказ. Вопрос какой-то неправильный. Кроме того, я думаю, что приведенные выше идеи — единственные решения, которые будут работать с *args и **kwargs. Я думаю, что ответ все еще имеет некоторую ценность. - person luk32; 08.05.2015

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

myparams = {'attr1': 'value1', 'attr2': 'value2', 'redundant_key': 'value3'}
usable = { k:v for k, v in myparams if k in MyClass.__init__.__code__.co_varnames }
new_instance = MyClass(**usable)

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

PS. Обратите внимание, что у вас возникнут проблемы, если один из ваших словарей будет содержать ключ self. :-) Вы можете отфильтровать его, если не можете доверять своему источнику данных.

person alexis    schedule 08.05.2015
comment
Это действительно хорошее решение, но проблема в том, что класс, который у меня есть, создается с помощью sqlalchemy. Я просто пишу это в соответствии с декларативным стилем определения класса. Итак, MyClass.__init__.__code__.co_varnames возвращает что-то вроде этого ('self', 'kwargs', 'new_state'), и я просто не могу написать метод __init__ так, как хочу - person Demyanov; 08.05.2015
comment
В таком случае ваш вопрос должен относиться конкретно к скальхимии! Вам придется покопаться в его внутренностях, чтобы выяснить, какие аргументы принимаются интересующими вас классами. Возможно, вы сможете что-то использовать (возможно, его инициализаторы имеют стандартный способ управления разрешенными аргументами ключевого слова), или нет. В общем, вы не можете ожидать, что узнаете, что будет делать функция, не запустив ее или не смоделировав ее. - person alexis; 08.05.2015
comment
Я также сильно подозреваю, что создаваемые атрибуты будут зависеть от предоставленных вами аргументов ключевого слова, а это означает, что даже если вы можете создать экземпляр конструктора без аргументов для создания экземпляра объекта-образца, вы не увидите все возможные атрибуты в экземпляре объекта. - person alexis; 08.05.2015

Вы должны были упомянуть, что вам нужны столбцы класса SQLAlchemy-Mapper. Это гораздо более простая задача:

dict = {'attr1': 'value1', 'attr2': 'value2', 'redundant_key': 'value3'}
columns = YourTable.__table__.columns.keys()
entry = YourTable(**{k: dict[k] for k in columns})
person Daniel    schedule 08.05.2015