__repr__ 和 __str__ 在动态 class 构造中不起作用

__repr__ and __str__ not working in dynamic class construction

我正在 Python 控制台中构建一个交互式文件资源管理器,这样当我传入一个路径时,我得到一个对象,然后用一个点 . 开始自动完成建议路径的内容,然后我再次执行此操作以获取子文件夹的内容,依此类推,直到我找到文件并且它 returns 路径。

我已经实现了我的目标,除了这个小烦人的事情:我想要 __repr__ 方法,但它从来没有奏效。

这是我的代码:

import os
from glob import glob

path = r'C:\Users\eng_a\Downloads'

def browse(path):
    my_dict = {'_path': path}
    tmp = os.listdir(path)
    key_contents = []
    for akey in tmp:
        key_contents.append(akey.replace(".", "_").replace(" ", "_").replace("-", "_"))
    val_paths = glob(path + '//*')
    for akey, avalue in zip(key_contents, val_paths):
        if os.path.isfile(avalue):
            my_dict[akey] = avalue
        else:
            my_dict[akey] = browse(avalue)

    def func(self):
        return self._path
    my_dict["__repr__"] = func
    my_dict["__str__"] = func
    obj = type(os.path.basename(path), (), dict(zip(my_dict.keys(), my_dict.values())))
    return obj
>>> b = browse(path)
>>> b

不幸的是,它一直在打印 __main__

如评论中所述,obj 是 class,不是实例。它包含一个函数 __repr__,该函数将在您创建实例后立即绑定到该实例。

一个简单而优雅的解决方案是将函数 browse 替换为同名的 class。调用 class 会创建一个实例(除非你真的搞砸了 metaclasses 或 __new__),所以你现在拥有的接口不必更改。然而,在内部,您将为您深入研究的每个目录实例化您的 class。

这将允许您做的另一件事是拥有一个真正动态的解决方案。现在你实际上递归到你的根的所有 children 。这在时间和内存方面都非常昂贵。理想情况下,您只想列出当前目录,并仅在被要求时递归到 children。

from os import listdir
from os.path import isdir, join
import re

class browse:
    def __init__(self, path, directory=True):
        # Create an attribute in __dict__ for each child
        self.__path__ = path
        if directory:
            for file in listdir(path):
                full = join(path, file)
                key = re.sub(r'^(?=\d)|\W', '_', file)
                setattr(self, key, full if isdir(full) else browse(full, False))

    def __getattribute__(self, name):
        if name == '__path__':
            return super().__getattribute__(name)
        d = super().__getattribute__('__dict__')
        if name in d:
            child = d[name]
            if isinstance(child, str):
                child = browse(child)
                setattr(self, name, child)
            return child
        return super().__getattribute__(name)

    def __repr__(self):
        return self.__path__

    def __str__(self):
        return self.__path__

此解决方案确实为根路径中的每个条目添加了一个属性。文件记录为 browse objects,而目录记录为字符串。覆盖 __getattribute__ 允许您即时交换完整 browse objects 的字符串,而不必预先展开所有文件夹。

考虑到预期的用例,可能的改进是删除行 setattr(self, name, child)。这样,您就不会保留对您不小心浏览到的目录的不必要引用,例如。