可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Here is the link to my first question: Creating class instance from dictionary?
So I am trying to create class instance from dictionary which holds keys that class has not. For example:
class MyClass(object):
def __init__(self, value1, value2):
self.attr1 = value1
self.attr2 = value2
dict = {'attr1': 'value1', 'attr2': 'value2', 'redundant_key': 'value3'}
Before creating class instance I have to delete this redundant_key
from dict
dict.pop('redundant_key', None)
new_instance = MyClass(**dict)
The problem is that I have several classes and several dicts with a lot of keys (I have several responses in json format which are represented as dicts and I want to create objects from this json responses). I already found an interim solution from previous question - how to delete redundant keys. I can create new dict from old dict only with keys that I need:
new_dict = {key: old_dict[key] for key in allowed_keys}
So here is the code:
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})
All I need now is to get allowed_keys
. So the question - Is there any way to get class instance attributes without creating class instance?
回答1:
If you insist on using an overly general dictionary to initialize your object, just define __init__
to accept, but ignore, the extra keys.
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)
If you can't modify __init__
(as appears to be the case if it inherits from a SQLAlchemy declarative base), add an alternate constructor to accept all the keyword arguments but pick out the ones you need.
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)
回答2:
Disclaimer: This answers what OP was asking about (getting attributes of an instance), not what they needed. Which seems to be constructor's parameter list.
You cannot do what you want. In python attributes are added to an instance of a class dynamically. Two instances of the same class can have different attributes. Well... to be precise, there are things called instance attributes, and class attributes.
Instance attributes are the ones associated with the instance of a class. Class attributes are associated with its definition i.e. if you wrote (MyClass.foo
)
If you look at the example, the attributes are added during the __init__
on self
so they are instance attributes.
What you could do is to create a valid instance and inspect it (look at below example), provide a static list of allowed_keys
(e.g. as a class attribute), or in some way require allowed_keys
as constructor parameter. All of these are kind of workarounds as what you really need is impossible. They have their pros and cons.
example:
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)
回答3:
Here's how to do what you're trying to do: Instantiate your classes with appropriate arguments. Note that you're not examining the attributes that __init__
creates, but the arguments it accepts. It doesn't matter what it does with them-- that's not your concern when you're calling the constructor.
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)
I used a filtered dictionary comprehension for clarity, but you could do it in one step of course.
PS. Note that you'll have a problem if one of your dictionaries happens to contain the key self
. :-) You may want to filter it out if you can't trust your data source.
回答4:
You should have mentioned, that you want the columns of a SQLAlchemy-Mapper-Class. This is a much easier task:
dict = {'attr1': 'value1', 'attr2': 'value2', 'redundant_key': 'value3'}
columns = YourTable.__table__.columns.keys()
entry = YourTable(**{k: dict[k] for k in columns})