当前位置: 首页 > 面试题库 >

错误使用__new__生成类?

西门安歌
2023-03-14
问题内容

我正在创建一些类来处理各种类型的文件共享(nfs,afp,s3,本地磁盘)等中的文件名。当用户输入时,会得到一个标识数据源(即"nfs://192.168.1.3""s3://mybucket/data")的字符串

我从具有通用代码的基类中继承特定文件系统。我感到困惑的是对象创建。我有以下内容:

import os

class FileSystem(object):
    class NoAccess(Exception):
        pass

    def __new__(cls,path):
        if cls is FileSystem:
            if path.upper().startswith('NFS://'): 
                return super(FileSystem,cls).__new__(Nfs)
            else: 
                return super(FileSystem,cls).__new__(LocalDrive)
        else:
            return super(FileSystem,cls).__new__(cls,path)

    def count_files(self):
        raise NotImplementedError

class Nfs(FileSystem):
    def __init__ (self,path):
        pass

    def count_files(self):
        pass

class LocalDrive(FileSystem):
    def __init__(self,path):
        if not os.access(path, os.R_OK):
            raise FileSystem.NoAccess('Cannot read directory')
        self.path = path

    def count_files(self):
        return len([x for x in os.listdir(self.path) if os.path.isfile(os.path.join(self.path, x))])

data1 = FileSystem('nfs://192.168.1.18')
data2 = FileSystem('/var/log')

print type(data1)
print type(data2)

print data2.count_files()

我以为这会很好用,__new__但我读过的大多数文章都劝阻它。有没有更公认的方法来解决此问题?


问题答案:


认为用__new__()做你想要的东西是不正确的。换句话说,我不同意这个问题)的公认答案,该观点认为工厂功能始终是“最佳方法”。

如果您真的想避免使用它,则唯一的选择是元类或单独的工厂函数/方法。考虑到可用的选择,将__new__()方法设置为一种(因为默认情况下是静态的)是一种完全明智的方法。

也就是说,以下是我认为是代码的改进版本。我添加了几个类方法来帮助自动查找所有子类。它们支持更好的最重要方法-
现在添加子类不需要修改__new__()方法。这意味着它现在很容易扩展,因为它有效地支持了所谓的 虚拟构造函数

也可以使用类似的实现将实例的创建从__new__()方法中移到单独的(静态)工厂方法中-
因此,从某种意义上说,无论使用什么名称,所示技术都是一种相对简单的编码可扩展通用工厂函数的方法它给出了。

# Works in Python 2 and 3.

import os
import re

class FileSystem(object):
    class NoAccess(Exception): pass
    class Unknown(Exception): pass

    # Regex for matching "xxx://" where x is any non-whitespace character except for ":".
    _PATH_PREFIX_PATTERN = re.compile(r'\s*([^:]+)://')

    @classmethod
    def _get_all_subclasses(cls):
        """ Recursive generator of all class' subclasses. """
        for subclass in cls.__subclasses__():
            yield subclass
            for subclass in subclass._get_all_subclasses():
                yield subclass

    @classmethod
    def _get_prefix(cls, s):
        """ Extract any file system prefix at beginning of string s and
            return a lowercase version of it or None when there isn't one.
        """
        match = cls._PATH_PREFIX_PATTERN.match(s)
        return match.group(1).lower() if match else None

    def __new__(cls, path):
        """ Create instance of appropriate subclass using path prefix. """
        path_prefix = cls._get_prefix(path)

        for subclass in cls._get_all_subclasses():
            if subclass.prefix == path_prefix:
                # Using "object" base class method avoids recursion here.
                return object.__new__(subclass)
        else:  # No subclass with matching prefix found (& no default defined)
            raise FileSystem.Unknown(
                'path "{}" has no known file system prefix'.format(path))

    def count_files(self):
        raise NotImplementedError


class Nfs(FileSystem):
    prefix = 'nfs'

    def __init__ (self, path):
        pass

    def count_files(self):
        pass


class LocalDrive(FileSystem):
    prefix = None  # Default when no file system prefix is found.

    def __init__(self, path):
        if not os.access(path, os.R_OK):
            raise FileSystem.NoAccess('Cannot read directory')
        self.path = path

    def count_files(self):
        return sum(os.path.isfile(os.path.join(self.path, filename))
                     for filename in os.listdir(self.path))


if __name__ == '__main__':

    data1 = FileSystem('nfs://192.168.1.18')
    data2 = FileSystem('c:/')  # Change as necessary for testing.

    print(type(data1).__name__)  # -> Nfs
    print(type(data2).__name__)  # -> LocalDrive

    print(data2.count_files())  # -> <some number>

Python 3.6+更新

上面的代码可在Python 2和3.x中使用。但是,在Python
3.6中,object为named添加了一个新的类方法,__init_subclass__()该方法通过使用子类自动创建子类的“注册表”来简化子类的查找,而不必像上述_get_all_subclasses()方法那样递归地检查每个子类。

# Requires Python 3.6+

import os
import re

class FileSystem(object):
    class NoAccess(Exception): pass
    class Unknown(Exception): pass

    # Regex for matching "xxx://" where x is any non-whitespace character except for ":".
    _PATH_PREFIX_PATTERN = re.compile(r'\s*([^:]+)://')
    _registry = {}  # Registered subclasses.

    @classmethod
    def __init_subclass__(cls, /, path_prefix, **kwargs):
        super().__init_subclass__(**kwargs)
        cls._registry[path_prefix] = cls  # Add class to registry.

    @classmethod
    def _get_prefix(cls, s):
        """ Extract any file system prefix at beginning of string s and
            return a lowercase version of it or None when there isn't one.
        """
        match = cls._PATH_PREFIX_PATTERN.match(s)
        return match.group(1).lower() if match else None

    def __new__(cls, path):
        """ Create instance of appropriate subclass. """
        path_prefix = cls._get_prefix(path)
        subclass = FileSystem._registry.get(path_prefix)
        if subclass:
            # Using "object" base class method avoids recursion here.
            return object.__new__(subclass)
        else:  # No subclass with matching prefix found (and no default).
            raise FileSystem.Unknown(
                f'path "{path}" has no known file system prefix')

    def count_files(self):
        raise NotImplementedError


class Nfs(FileSystem, path_prefix='nfs'):
    def __init__ (self, path):
        pass

    def count_files(self):
        pass


class LocalDrive(FileSystem, path_prefix=None):  # Default file system.
    def __init__(self, path):
        if not os.access(path, os.R_OK):
            raise FileSystem.NoAccess('Cannot read directory')
        self.path = path

    def count_files(self):
        return sum(os.path.isfile(os.path.join(self.path, filename))
                     for filename in os.listdir(self.path))


if __name__ == '__main__':

    data1 = FileSystem('nfs://192.168.1.18')
    data2 = FileSystem('c:/')  # Change as necessary for testing.

    print(type(data1).__name__)  # -> Nfs
    print(type(data2).__name__)  # -> LocalDrive

    print(data2.count_files())  # -> <some number>

    try:
        data3 = FileSystem('foobar://42')  # Unregistered path prefix.
    except FileSystem.Unknown as exc:
        print(str(exc), '- raised as expected')
    else:
        raise RuntimeError(
              "Unregistered path prefix should have raised Exception!")


 类似资料:
  • 我有以下代码片段 我尝试运行应用程序时出现以下错误(部分) 似乎type="num "没有被处理。我怀疑年龄int也可能是一个问题,因为它是一个int,但需要一个string。从sting到int的反向转换也可能是一个问题。 任何帮助是值得赞赏的。 谢谢

  • 问题内容: 我试图让到效果的,然后(在Windows 7; ñ 。一个透明安装了Cygwin所有X命令礼貌 ñ x的命令决心在命令行就好了)。 最初,我使用,但是想捕获stdout / stderr信息,所以我想使用,然后重写代码以使用它。但是,这破坏了一切。 重写的命令变为: 但是,运行此命令将产生以下错误: 重写的命令变为: 但是,运行此命令将产生以下错误: 如何使spawn运行可以正常使用的

  • 我创建了一个简单的. proto文件并执行了编译器(protoc-2.5.0rc1-win32.zip)。为规定的包生成了一个java文件。但是,生成的文件不会编译。. proto文件很简单,只有一条消息,其中包含一堆简单类型的可选字段(正确编号)。. java文件不编译,例如: 构造函数生成了消息。FieldAccessorTable(Descriptors.Descriptor,字符串[])未

  • 我正在寻找变量的差异。当差异大于时,我希望将其标记为。以下是我尝试的: 为什么在这里被标记为?

  • 我需要在我的项目中使用一个web服务。我使用NetBeans所以我右键单击我的项目并尝试添加一个新的“Web服务客户端”。上次我检查时,这是创建web服务客户机的方法。但它导致一个AssertionError,它说: java.lang.AssertionError:org.xml.sax.SAXParseException;systemid:jar:file:/path/to/glassfish

  • 我有一个JS应用程序。它在linux上运行良好,但在Windows10中我遇到了一个错误。 不正确的代码是 我在github中找到了这个错误的原因,我想问题是spawn nodejs spawn Doc在windows中无法正常工作。但我不知道如何修改这段代码使其工作。有人能帮我吗?