NameSpacing:如何根据外类的类变量设置内部类的类变量?

时间:2018-03-16 12:26:25

标签: python namespaces

我试图"模拟" python中的命名空间。我使用内部和外部类hirarchies来创建我的命名空间。例如,您希望在一个位置保存文件路径(如资源)。我试过这样的事情:

src = #path to source folder

class Resources:
    root = src + "Resources\\"

    class Fonts:
        root = Resources.root + "fonts\\"
        font1 = root + "font1.ttf"
        font2 = root + "font2.ttf"     

    class Images:
        root = Resources.root + "images\\"
        logo = root + "logo"
        image1= root + "image1"

    class StyleSheets:
        root = Resources.root + "stylesheets\\"
        default = root + "default.qss"

class JsonData:
    root = src + "Data\\"

    class TableEntries:
        root = JsonData.root
        entries1 = root + "Entries1.json"
        entries2 = root + "Entries2.json"

访问元素如下所示:

logoPath = Resources.Images.image1

不幸的是,由于以下错误,这不起作用:

root = Resources.root + "fonts\\"
NameError: name 'Resources' is not defined

我的问题

是否可以根据外部类的类变量设置内部类的类变量?如果没有,是否有另一种方法来访问如上所示的元素而不使用多个文件?

2 个答案:

答案 0 :(得分:1)

  

是否可以根据外部类的类变量设置内部类的类变量?

不是没有重新定位到自定义元类来处理内部类,这肯定不会有助于可读性和可维护性(并且 - 任何有经验的python程序员都可以看作是一个完整的WTF)。

编辑: 实际上,对于您的示例代码段,元类解决方案并不复杂,请参阅此答案的结尾

原因是在Python中几乎所有事情都发生在运行时。 class是一个可执行语句,只有在整个类语句的主体结束后才创建并绑定到它的名称​​的类对象。

  

如果没有,是否有其他方法可以访问如上所示的元素而不使用多个文件?

非常简单(愚蠢的例子):

import os

# use a single leading underscore to mark those classes
# as "private" (=> not part of the module's API)
class _Fonts(object):
    def __init__(self, resource):
        self.font1 = os.path.join(resource.root, "font1.ttf")
        self.font2 = os.path.join(resource.root, "font2.ttf")

class _Resources(object):
    def __init__(self, src):
        self.root = os.path.join(rsc, "Ressources")
        self.Fonts = _Fonts(self)

# then instanciate it like any other class
src = "/path/to/source/folder"
Resources = _Resources(src)

print(Resources.Fonts.font1)
编辑:经过一番思考后,基于元类的解决方案可能不会那么复杂(但这不是通用的):

import os

class ResourcesMeta(type):
    def __init__(cls, name, bases, attrs):
        for name in attrs:
            obj = getattr(cls, name)
            if isinstance(obj, type) and issubclass(obj, SubResource):
                instance = obj(cls)
                setattr(cls, name, instance)


class SubResourceMeta(type):
    def __new__(meta, name, bases, attrs):
        if not bases:
            # handle the case of the SubResource base class
            return type.__new__(meta, name, bases, attrs)

        root = attrs.pop("root")
        cls = type.__new__(meta, name, bases, {})
        cls._root = root
        cls._attrs = attrs
        return cls

class SubResource(metaclass=SubResourceMeta):
    def __init__(self, parent):
        self.root = os.path.join(parent.root, self._root)
        for name, value in self._attrs.items():
            setattr(self, name, os.path.join(self.root, value))


class Resources(metaclass=ResourcesMeta):
    root = "/path/to/somewhere"

    class Fonts(SubResource):
        root = "fonts"
        font1 = "font1.ttf"
        font2 = "font2.ttf"

    class Images(SubResource):
        root = "images"
        logo = "logo"
        image1= "image1"

答案 1 :(得分:0)

我认为你没有在OOP中明确类和实例的概念。如果你想存储这类信息Resources不应该是一个类,它应该是一个Dir类的实例。

class Dir:
    def __init__(self, path="/", parent=None):
        self.parent = parent
        self.path = path
        self.contents = {}
    def __getitem__(self, key):
        return self.contents[key]
    def create_subdir(name):
        self.contents[name] = Dir(os.path.join(self.path + name), self)
    def add_file(file):
        self.contents[file] = file  # You should probably also have a File type
    # ...

resources = Dir(os.path.join(src, "Resources"))
resources.create_subdir("fonts")
fonts = resources["fonts"]
fonts.add_file("font1.ttf")
...

我已经使用os.path.join函数委托Python为每个SO选择正确的分隔符,而不是像你一样硬编码Windows分隔符。 __getitem__方法允许获取项目,就好像变量是字典一样。

编辑:

如果您不喜欢路径库的pathlib,您可以利用div operator usage标准模块并添加属性访问表示法(使用'。'来访问子目录)。

from pathlib import Path as Path_, WindowsPath as WPath_, PosixPath as PPath_
import os

class Path(Path_):
    def __new__(cls, *args, **kwargs):
        return super().__new__(WindowsPath if os.name == 'nt' else PosixPath,
                               *args, **kwargs)

    def __getattr__(self, item):
        if item == '_str':
            raise AttributeError
        for i in self.iterdir():
            if i.name == item:
                return i
        raise AttributeError

class WindowsPath(WPath_, Path):
    pass

class PosixPath(PPath_, Path):
    pass

current = Path()
subdir = current.subdir_name  # current / 'subdir_name'