Python 中 Java 的 getClass().getFields() 的等效方法是什么?

6 投票
3 回答
6662 浏览
提问于 2025-04-16 18:23

我正在把一段代码从Java转换成Python,但我不知道怎么把下面的部分翻译过来:

Field[] fields = getClass().getFields();
    for (int i = 0; i < fields.length; i++ ) {
        if (fields[i].getName().startsWith((String) param){ ....

3 个回答

4

这不是完全一样的功能,但你可以用 dir(self) 来开始尝试。

8

在Python中,你可以通过 __dict__ 来查看一个对象的属性和绑定,比如:

>>> class A:
...     def foo(self): return "bar"
...
>>> A.__dict__
{'__module__': '__main__', 'foo': <function foo at 0x7ff3d79c>, '__doc__': None}

另外,从C#的角度也有人问过这个问题,具体可以参考这个链接: 如何列举Python中一个对象的属性?

除了直接使用 __dict__,你还可以使用 inspect.getmembers(object[, predicate]),这个方法有一些很实用的功能,比如 inspect.ismethod(object)

8

首先,我想强调的是,Python中没有像 getClass().getFields() 这样的东西,因为一个对象可以有很多不是由类定义的字段。实际上,在Python中创建一个字段只需要给它赋值。字段不是被“定义”的,而是被“创建”的:

>>> class Foo(object):
...     def __init__(self, value):
...         # The __init__ method will create a field
...         self.value = value
... 
>>> foo = Foo(42)
>>> foo.value
42
>>> # Accessing inexistent field
... foo.another_value
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: 'Foo' object has no attribute 'another_value'
>>> # Creating the field
... foo.another_value = 34
>>> # Now I can use it
... foo.another_value
34

所以,你不是从类中获取字段,而是从对象中获取字段。

另外,Python的方法其实就是一些特殊值的字段。方法只是函数的实例:

>>> type(foo.__init__)

需要注意的是,Python中没有 getClass().getMethods() 这样的函数,而 getClass().getFields() 的“等价物”也会返回方法。

那么,如何获取字段(在Python中通常称为属性)呢?当然,你不能从类中获取它们,因为对象才存储这些属性。所以你可以使用 dir() 函数来获取对象的属性名称:

>>> dir(foo)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', 
 '__getattribute__', '__hash__', '__init__', '__module__', '__new__', 
 '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', 
 '__str__', '__subclasshook__', '__weakref__', 'another_value', 'value']

一旦你得到了属性名称,就可以使用 getattr() 函数来获取每一个属性:

>>> getattr(foo, 'value')
42

要获取所有属性,你可以使用列表推导式

>>> [getattr(foo, attrname) for attrname in dir(foo)]
[<class '__main__.Foo'>, <method-wrapper '__delattr__' of Foo object at 0x2e36b0>,
 {'another_value': 34, 'value': 42}, None, <built-in method __format__ of Foo object at 0x2e36b0>, 
 <method-wrapper '__getattribute__' of Foo object at 0x2e36b0>, 
 ... # Lots of stuff
 34, 42]

最后,你可以找到你在某些属性上设置的值。

不过,这个列表也会包含方法。记住,它们也是属性。在这种情况下,我们可以让我们的列表推导式避免可调用的属性:

>>> [attrname for attrname in dir(foo) if not callable(getattr(foo, attrname))]
['__dict__', '__doc__', '__module__', '__weakref__', 'another_value', 'value']

现在,获取实际的值:

>>> [getattr(foo, attrname) for attrname in dir(foo)
...      if not callable(getattr(foo, attrname))]
[{'another_value': 34, 'value': 42}, None, '__main__', None, 34, 42]

那里仍然会有一些奇怪的值,比如 __dict____doc__ 等等。这些是你可能想要忽略的东西。如果是这样,只需在你的列表推导式中添加另一个条件:

>>> [attrname for attrname in dir(foo)
...     if not attrname.startswith('__') and
...         not callable(getattr(foo, attrname))]
['another_value', 'value']
>>> [getattr(foo, attrname) for attrname in dir(foo)
...     if not attrname.startswith('__') and
...         not callable(getattr(foo, attrname))]
[34, 42]

还有其他方法可以做到这些。例如,你可以查看对象的 __dict____slots__ 属性。不过,我觉得我介绍的方法对初学者来说更清晰。

编辑 还有两点。首先,cls解决方案非常好,因为它建议你查看inspect模块

另外,你可能想要获取属性的名称和它的值。你可以通过生成一个元组列表来实现:

>>> [(attrname, getattr(foo, attrname)) for attrname in dir(foo)
...     if not attrname.startswith('__') and
...         not callable(getattr(foo, attrname))]
[('another_value', 34), ('value', 42)]

幸运的是,inspect.getmembers() 函数是cls推荐的,它做得更好。

>>> import inspect
>>> inspect.getmembers(foo)
[('__class__', <class '__main__.Foo'>),
 # ... Lots of stuff ...
 ('another_value', 34), ('value', 42)]

要去掉方法,只需避免可调用的:

>>> inspect.getmembers(foo, lambda attr: not callable(attr))
[('__dict__', {'another_value': 34, 'value': 42}), ('__doc__', None), ('__module__', '__main__'), ('__weakref__', None), ('another_value', 34), ('value', 42)]

(不幸的是,inspect.ismethod() 没有按我预期的那样工作。)

还有很多内部内容,我们不能像处理方法那样直接获取它们。没有什么是列表推导式不能解决的:

>>> [(name, value) for name, value in inspect.getmembers(foo, lambda attr: not callable(attr))
...         if not name.startswith('__')]
[('another_value', 34), ('value', 42)]

Python是一种非常动态的语言,这种解决方案在某些情况下可能也不适用。考虑到可能会有一个对象需要存储一个函数以便在某处使用。函数是可调用对象,而这个属性将不会被呈现。然而,从“逻辑上”来说,它是一个属性,是要使用的数据。你应该记住这一点。不过,我敢打赌你不会经常遇到这样的问题。

希望这对你有帮助。

撰写回答