英文:
How to access attributes of a Python class as one would access fields of a Matlab structure in an iterative context
问题
在Matlab中,让我们考虑一个名为"structure"的结构体和一个字段列表"list_of_fields"。
structure = struct;
list_of_fields = ["a","b","d"]; % 结构体的一些特定字段,而不是所有字段
for field = list_of_fields
structure.(field) = 0;
end
我想在Python中以类的属性来实现相同的功能:而不是编写如下代码:
class Example:
def __init__(self):
self.a = 0
self.b = 0
self.d = 0
我的目标是编写一个循环,它遍历特定属性a、b和d(而不是其他属性)。如何在Python中实现这一目标?
到目前为止,我是按照前述Python示例中的方式访问所有属性。
英文:
In Matlab, let's consider a structure "structure" and a list of fields "list_of_fields".
structure = struct;
list_of_fields = ["a","b","d"]; % Some specific fields of the structure and not all of them
for field = list_of_field
structure.(field) = 0;
end
I would like to do the same in Python with the attribute of a class: instead of writing:
class Example
def __init__(self) :
self.a = 0
self.b = 0
self.d = 0
My goal would be to write a loop that iterates over the specific attribute a, b and d (and no other attribute). How do you achieve that in Python ?
Up to now, I accessed all attribute as in the Python example aforementioned.
答案1
得分: 2
如我提到的,我认为你试图做的是一个非常糟糕的主意... 但如果你真的想要这样做,你可以定义__getitem__
方法,如果它在你感兴趣的值集合中,就返回该值,否则引发StopIteration
异常。这将允许使用for循环遍历这些属性,它不会返回该对象的方法或其他属性。
class Example:
def __init__(self):
self.a = 0
self.b = 1
self.c = 2
self.other_stuff = [1, None, False]
self.stuff = {"a": 2, "b": 3, "c": 87}
def __getitem__(self, index):
key = list(self.__dict__.keys())[index]
if key in {"a", "b", "c"}:
return self.__dict__[key]
else:
raise StopIteration()
def foo(self):
pass
e = Example()
for item in e:
print(f"{item=}")
print(e.__dict__)
关于为什么你不应该这样做的一些附注:
- 不清楚这样做的目的,它会引起头痛,比想象的早。
- 应该使用适当的数据结构,如
dict
、dataclass
等。 - 字典具有保证的插入顺序(Python 3.7+ 参见 https://stackoverflow.com/a/60775590/15923186),因此在创建
__dict__
时,必须以非常特定的顺序定义要遍历的属性,否则StopIteration
会过早引发,你将无法获得所有你想要的值。这也是非常糟糕的,因为这是一个实现细节,使用你的代码的任何人都不需要知道,从你的代码中看不出来。 - 要了解我的意思,只需更改顺序并像这样定义
Example
,然后再次查看e.__dict__
的输出:
class Example:
def __init__(self):
self.stuff = {"a": 2, "b": 3, "c": 87}
self.a = 0
self.b = 1
self.c = 2
self.other_stuff = [1, None, False]
...
...
print(e.__dict__)
这个变化后,代码将不再工作。
英文:
As I mentioned what you are trying to do is a very bad idea imho...
But if you really want it you can define __getitem__
method and return value if it's in let's say set of values of your interest and raise StopIteration
otherwise.
This will allow to loop over those attributes using for loop and it won't return neither methods nor other attributes of that object.
class Example:
def __init__(self):
self.a = 0
self.b = 1
self.c = 2
self.other_stuff = [1,None,False]
self.stuff = {"a":2,"b":3,"c":87}
def __getitem__(self, index):
key = list(self.__dict__.keys())[index]
if key in {"a", "b", "c"}:
return self.__dict__[key]
else:
raise StopIteration()
def foo(self):pass
e = Example()
for item in e:
print(f"{item=}")
print(e.__dict__)
Side notes and why you should NOT do that:
- it's unclear what's the purpose of this and it's gonna cause a headache sooner than later
- proper data structures should be used such as
dict
s,dataclass
es and so on - the dictionaries have guaranteed insert order (python 3.7+ see https://stackoverflow.com/a/60775590/15923186), so when the
__dict__
is created you have to define those attributes you want to loop through in the very specific order, otherwise theStopIteration
will be raised too early and you won't get all the values you want. Which is again very bad, cause it's an implementation detail and anyone using your code doesn't have to know that, neither it's obvious from looking at your code.
To see what I mean just change the order and define Example
like this and again see the output of e.__dict__
:
class Example:
def __init__(self):
self.stuff = {"a":2,"b":3,"c":87}
self.a = 0
self.b = 1
self.c = 2
self.other_stuff = [1,None,False]
...
...
print(e.__dict__)
The code won't work anymore with that change
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论