如何在迭代上下文中访问Python类的属性,就像在Matlab结构中访问字段一样。

huangapple go评论86阅读模式
英文:

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__)

关于为什么你不应该这样做的一些附注:

  • 不清楚这样做的目的,它会引起头痛,比想象的早。
  • 应该使用适当的数据结构,如dictdataclass等。
  • 字典具有保证的插入顺序(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 dicts, dataclasses 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 the StopIteration 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

huangapple
  • 本文由 发表于 2023年3月7日 16:58:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/75659821.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定