【问题标题】:How to recursively `dir()` an object in Python 3.9.5?如何在 Python 3.9.5 中递归地`dir()` 一个对象?
【发布时间】:2021-06-03 06:36:17
【问题描述】:

我知道这个问题已经被问过很多次了,但我使用的是 Python 3.9.5,我在这里找到的解决方案都没有奏效。

我的请求非常简单:获取一个对象及其子对象的所有可用对象名称的 XML 树列表。

为了说明这一点,我安装了许多 Python 模块,并想知道模块提供的所有内容。

dir() 方法返回一个对象内部的属性名称列表,它返回一个字符串列表。

type() 方法返回对象的类型,一些子对象本身有子对象,我想要一个 XML 树列出最低级别对象的所有名称(并且只有名称,而不是值),即intboolfloatstrsetlisttupledict 等类型。

例如,假设我们有一个名为obj 的对象,obj 具有三个属性:abca 没有唯一属性,b 有两个唯一属性:dede 没有唯一属性,c 有三个唯一属性:@ 987654345@、gh,以及f 有一个名为i 的唯一属性,g 有一个名为j 的唯一属性,而h 有一个名为k 的唯一属性。 ijk 是内置类型。

现在结果应该是这样的:

<?xml version="1.0" ?>
<elem name='obj'>
    <attr name='a' />
    <elem name='obj.b'>
        <attr name='d' />
        <attr name='e' />
    </elem>
    <elem name='obj.c'>
        <elem name='obj.c.f'>
            <attr name='i' />
        </elem>
        <elem name='obj.c.g'>
            <attr name='j' />
        </elem>
        <elem name='obj.c.h'>
            <attr name='k' />
        </elem>
    </elem>
</elem>

标有elem的元素是具有__dict__属性的自定义类,标有attr的元素没有__dict__s。

现在要获取obj 的属性,我们使用dir(obj),它会返回一个字符串列表['a','b','c']

要获取obj.c 的属性,我们必须手动输入dir(obj.c),因为'c' 是一个字符串,而'obj.c' 不在vars() 中。

现在要将字符串转换为名称,我们必须使用eval()dir(eval('obj.c')) 正确执行。

但是要递归地使用该命令,我们必须使用类似这样的东西:

dir(eval(f'{obj.__name__}.{attr}'))

但是__name__ 属性没有给出其全名(如obj.c.f),如果名称包含__,则__name__ 值将没有__s。更何况有些对象没有__name__ 属性。

那么如何做到这一点呢?


更新

我已经取得了一些初步的成功:

import inspect
from xml.dom.minidom import Document

doc = Document()

def treeobj(obj, path=''):
    fullname = f'{path}.{obj.__name__}'.lstrip('.')
    node = doc.createElement('elem')
    node.setAttribute('name', fullname)
    for attr in dir(obj):
        if not attr.startswith('__'):
            val = getattr(obj, attr)
            if inspect.isclass(val):
                elem = treeobj(val, fullname)
            else:
                elem = doc.createElement('attr')
                elem.setAttribute('name', attr)
            node.appendChild(elem)
    return node

这是print(treeobj(os).toprettyxml())的输出:

<elem name="os">
        <elem name="os.DirEntry">
                <attr name="inode"/>
                <attr name="is_dir"/>
                <attr name="is_file"/>
                <attr name="is_symlink"/>
                <attr name="name"/>
                <attr name="path"/>
                <attr name="stat"/>
        </elem>
        <attr name="F_OK"/>
        <elem name="os.GenericAlias"/>
        <elem name="os.Mapping">
                <attr name="_abc_impl"/>
                <attr name="get"/>
                <attr name="items"/>
                <attr name="keys"/>
                <attr name="values"/>
        </elem>
        <elem name="os.MutableMapping">
                <attr name="_MutableMapping__marker"/>
                <attr name="_abc_impl"/>
                <attr name="clear"/>
                <attr name="get"/>
                <attr name="items"/>
                <attr name="keys"/>
                <attr name="pop"/>
                <attr name="popitem"/>
                <attr name="setdefault"/>
                <attr name="update"/>
                <attr name="values"/>
        </elem>
        <attr name="O_APPEND"/>
        <attr name="O_BINARY"/>
        <attr name="O_CREAT"/>
        <attr name="O_EXCL"/>
        <attr name="O_NOINHERIT"/>
        <attr name="O_RANDOM"/>
        <attr name="O_RDONLY"/>
        <attr name="O_RDWR"/>
        <attr name="O_SEQUENTIAL"/>
        <attr name="O_SHORT_LIVED"/>
        <attr name="O_TEMPORARY"/>
        <attr name="O_TEXT"/>
        <attr name="O_TRUNC"/>
        <attr name="O_WRONLY"/>
        <attr name="P_DETACH"/>
        <attr name="P_NOWAIT"/>
        <attr name="P_NOWAITO"/>
        <attr name="P_OVERLAY"/>
        <attr name="P_WAIT"/>
        <elem name="os.PathLike">
                <attr name="_abc_impl"/>
        </elem>
        <attr name="R_OK"/>
        <attr name="SEEK_CUR"/>
        <attr name="SEEK_END"/>
        <attr name="SEEK_SET"/>
        <attr name="TMP_MAX"/>
        <attr name="W_OK"/>
        <attr name="X_OK"/>
        <elem name="os._AddedDllDirectory">
                <attr name="close"/>
        </elem>
        <elem name="os._Environ">
                <attr name="_MutableMapping__marker"/>
                <attr name="_abc_impl"/>
                <attr name="clear"/>
                <attr name="copy"/>
                <attr name="get"/>
                <attr name="items"/>
                <attr name="keys"/>
                <attr name="pop"/>
                <attr name="popitem"/>
                <attr name="setdefault"/>
                <attr name="update"/>
                <attr name="values"/>
        </elem>
        <attr name="_check_methods"/>
        <attr name="_execvpe"/>
        <attr name="_exists"/>
        <attr name="_exit"/>
        <attr name="_fspath"/>
        <attr name="_get_exports_list"/>
        <attr name="_walk"/>
        <elem name="os._wrap_close">
                <attr name="close"/>
        </elem>
        <attr name="abc"/>
        <attr name="abort"/>
        <attr name="access"/>
        <attr name="add_dll_directory"/>
        <attr name="altsep"/>
        <attr name="chdir"/>
        <attr name="chmod"/>
        <attr name="close"/>
        <attr name="closerange"/>
        <attr name="cpu_count"/>
        <attr name="curdir"/>
        <attr name="defpath"/>
        <attr name="device_encoding"/>
        <attr name="devnull"/>
        <attr name="dup"/>
        <attr name="dup2"/>
        <attr name="environ"/>
        <elem name="os.OSError">
                <attr name="args"/>
                <attr name="characters_written"/>
                <attr name="errno"/>
                <attr name="filename"/>
                <attr name="filename2"/>
                <attr name="strerror"/>
                <attr name="winerror"/>
                <attr name="with_traceback"/>
        </elem>
        <attr name="execl"/>
        <attr name="execle"/>
        <attr name="execlp"/>
        <attr name="execlpe"/>
        <attr name="execv"/>
        <attr name="execve"/>
        <attr name="execvp"/>
        <attr name="execvpe"/>
        <attr name="extsep"/>
        <attr name="fdopen"/>
        <attr name="fsdecode"/>
        <attr name="fsencode"/>
        <attr name="fspath"/>
        <attr name="fstat"/>
        <attr name="fsync"/>
        <attr name="ftruncate"/>
        <attr name="get_exec_path"/>
        <attr name="get_handle_inheritable"/>
        <attr name="get_inheritable"/>
        <attr name="get_terminal_size"/>
        <attr name="getcwd"/>
        <attr name="getcwdb"/>
        <attr name="getenv"/>
        <attr name="getlogin"/>
        <attr name="getpid"/>
        <attr name="getppid"/>
        <attr name="isatty"/>
        <attr name="kill"/>
        <attr name="linesep"/>
        <attr name="link"/>
        <attr name="listdir"/>
        <attr name="lseek"/>
        <attr name="lstat"/>
        <attr name="makedirs"/>
        <attr name="mkdir"/>
        <attr name="name"/>
        <attr name="open"/>
        <attr name="pardir"/>
        <attr name="path"/>
        <attr name="pathsep"/>
        <attr name="pipe"/>
        <attr name="popen"/>
        <attr name="putenv"/>
        <attr name="read"/>
        <attr name="readlink"/>
        <attr name="remove"/>
        <attr name="removedirs"/>
        <attr name="rename"/>
        <attr name="renames"/>
        <attr name="replace"/>
        <attr name="rmdir"/>
        <attr name="scandir"/>
        <attr name="sep"/>
        <attr name="set_handle_inheritable"/>
        <attr name="set_inheritable"/>
        <attr name="spawnl"/>
        <attr name="spawnle"/>
        <attr name="spawnv"/>
        <attr name="spawnve"/>
        <attr name="st"/>
        <attr name="startfile"/>
        <attr name="stat"/>
        <elem name="os.stat_result">
                <attr name="count"/>
                <attr name="index"/>
                <attr name="n_fields"/>
                <attr name="n_sequence_fields"/>
                <attr name="n_unnamed_fields"/>
                <attr name="st_atime"/>
                <attr name="st_atime_ns"/>
                <attr name="st_ctime"/>
                <attr name="st_ctime_ns"/>
                <attr name="st_dev"/>
                <attr name="st_file_attributes"/>
                <attr name="st_gid"/>
                <attr name="st_ino"/>
                <attr name="st_mode"/>
                <attr name="st_mtime"/>
                <attr name="st_mtime_ns"/>
                <attr name="st_nlink"/>
                <attr name="st_reparse_tag"/>
                <attr name="st_size"/>
                <attr name="st_uid"/>
        </elem>
        <elem name="os.statvfs_result">
                <attr name="count"/>
                <attr name="f_bavail"/>
                <attr name="f_bfree"/>
                <attr name="f_blocks"/>
                <attr name="f_bsize"/>
                <attr name="f_favail"/>
                <attr name="f_ffree"/>
                <attr name="f_files"/>
                <attr name="f_flag"/>
                <attr name="f_frsize"/>
                <attr name="f_fsid"/>
                <attr name="f_namemax"/>
                <attr name="index"/>
                <attr name="n_fields"/>
                <attr name="n_sequence_fields"/>
                <attr name="n_unnamed_fields"/>
        </elem>
        <attr name="strerror"/>
        <attr name="supports_bytes_environ"/>
        <attr name="supports_dir_fd"/>
        <attr name="supports_effective_ids"/>
        <attr name="supports_fd"/>
        <attr name="supports_follow_symlinks"/>
        <attr name="symlink"/>
        <attr name="sys"/>
        <attr name="system"/>
        <elem name="os.terminal_size">
                <attr name="columns"/>
                <attr name="count"/>
                <attr name="index"/>
                <attr name="lines"/>
                <attr name="n_fields"/>
                <attr name="n_sequence_fields"/>
                <attr name="n_unnamed_fields"/>
        </elem>
        <attr name="times"/>
        <elem name="os.times_result">
                <attr name="children_system"/>
                <attr name="children_user"/>
                <attr name="count"/>
                <attr name="elapsed"/>
                <attr name="index"/>
                <attr name="n_fields"/>
                <attr name="n_sequence_fields"/>
                <attr name="n_unnamed_fields"/>
                <attr name="system"/>
                <attr name="user"/>
        </elem>
        <attr name="truncate"/>
        <attr name="umask"/>
        <elem name="os.uname_result">
                <attr name="count"/>
                <attr name="index"/>
                <attr name="machine"/>
                <attr name="n_fields"/>
                <attr name="n_sequence_fields"/>
                <attr name="n_unnamed_fields"/>
                <attr name="nodename"/>
                <attr name="release"/>
                <attr name="sysname"/>
                <attr name="version"/>
        </elem>
        <attr name="unlink"/>
        <attr name="unsetenv"/>
        <attr name="urandom"/>
        <attr name="utime"/>
        <attr name="waitpid"/>
        <attr name="waitstatus_to_exitcode"/>
        <attr name="walk"/>
        <attr name="write"/>
</elem>

但是似乎没有深度超过 1 的嵌套类,所以我不确定我的代码是否正常工作,但代码不会在这里抛出错误。

但是treeobj(PyQt6.QtCore) 抛出错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in treeobj
  File "<stdin>", line 9, in treeobj
  File "<stdin>", line 7, in treeobj
AttributeError: 'TimerInfo' object attribute 'interval' is an instance attribute

如何修复我的代码?


getattr()函数访问PyQt6.QtCore.QAbstractEventDispatcher.TimerInfo对象的interval属性应该会报错,这是一个实例属性,getattr()不支持,所以报错。

什么是实例属性以及如何获取“普通属性”和实例属性的值?

【问题讨论】:

  • 不要使用 eval,使用 *attr() 函数进行对象属性操作,使用 literal_eval() 进行代码/结构评估。
  • 提供具体的 Python 代码来构建生成 XML 输出的对象会更有用;我可以告诉你nested class 将提供你想要的. 表示法(通过__qualname__)。

标签: python python-3.x


【解决方案1】:

嗯,我刚刚修复了我的代码。它现在按预期工作。虽然它仍然是基本的,但它正在运行。

我想要找到的是类属性,这些属性是由同一个类的每个对象共享的。

类属性通常不会改变,它们也不应该改变,但如果它们改变了,改变会影响同一个类的每个对象。

实例属性对每个对象都是唯一的,它们在__init__函数中声明,在创建对象时定义,在一个实例中的更改不会影响同一类的其他对象。

getattr 只能获取类属性,因为类属性由同一类的所有对象共享并且是预定义的,而实例属性对于每个对象都是唯一的,并且在对象创建之前没有定义。

dir() 方法通常调用类的__dir__() 方法,该方法以字符串的形式返回类中的属性名称列表,这使得以编程方式使用其返回值访问类的属性有点困难.

但是大多数类都有一个__dict__属性,它是类的第一级属性的字典,以字符串中的属性名称作为键,就像dir()返回的一样,但是键有实际引用将相应的对象作为值传递给各个对象,从而可以轻松地从键中访问对象。

代码如下:

import importlib
import inspect
import sys
from xml.dom.minidom import Document

doc = Document()

def treeobj(obj, path=''):
    fullname = f'{path}.{obj.__name__}'.lstrip('.')
    node = doc.createElement('class')
    node.setAttribute('name', fullname)
    for attr in obj.__dict__.keys():
        if not attr.startswith('__'):
            val = obj.__dict__[attr]
            if inspect.isclass(val):
                elem = treeobj(val, fullname)
            else:
                elem = doc.createElement('attr')
                elem.setAttribute('name', attr)
            node.appendChild(elem)
    return node

if __name__ == '__main__':
    obj = importlib.import_module(sys.argv[1])
    doc.appendChild(treeobj(obj))
    print(doc.toprettyxml())

【讨论】:

    猜你喜欢
    • 2020-02-09
    • 1970-01-01
    • 1970-01-01
    • 2012-09-18
    • 1970-01-01
    • 2017-01-24
    • 1970-01-01
    • 2020-06-27
    • 2021-09-18
    相关资源
    最近更新 更多