【问题标题】:Module organization, inheritance, and @classmethods模块组织、继承和@classmethods
【发布时间】:2023-03-18 07:05:01
【问题描述】:

我正在尝试编写一个类似于内置函数和我见过的其他一些“成熟”Python 东西的类。我的 Pythonic 教育在课堂上有点参差不齐,我担心我把这一切都搞混了。

我想创建一个作为存储库的类,其中包含未处理文件(及其名称)的字典和已处理文件(及其名称)的字典。我想实现一些其他(子?)类来处理诸如打开和处理文件之类的事情。文件处理类应该能够更新主类中的字典。我还希望能够直接调用各个子模块,而不必单独实例化所有内容,例如:

import Pythia
p = Pythia()
p.FileManager.addFile("/path/to/some/file")

甚至

Pythia.FileManager.addFile("/path/to/some/file")

我一直在寻找关于@classmethod 和super 之类的东西,但我不能说我完全理解它。我也开始怀疑我可能有整个继承链向后 - 我认为我的主类实际上应该是处理和处理类的子类。我也想知道这是否会作为一个包更好地工作,但这是一个单独的、非常令人生畏的问题。

到目前为止,这是我的代码:

#!/usr/bin/python

import re
import os
class Pythia(object):
    def __init__(self):
        self.raw_files = {}
        self.parsed_files = {}
        self.FileManger = FileManager()
    def listf(self,fname,f):
        if fname in self.raw_files.keys():
            _isRaw = "raw"
        elif fname in self.parsed_files.keys():
            _isRaw = "parsed"
        else:
            return "Error: invalid file"
        print "{} ({}):{}...".format(fname,_isRaw,f[:100])

    def listRaw(self,n=None):
        max = n or len(self.raw_files.items())
        for item in self.raw_files.items()[:max]:
            listf(item[0],item[1])

    def listParsed(self,n=None):
        max = n or len(self.parsed_files.items())
        for item in self.parsed_files.items()[:max]:
            listf(item[0],item[1])

class FileManager(Pythia):
    def __init__(self):
        pass
    def addFile(self,f,name=None,recurse=True,*args):
        if name:
            fname = name
        else:
            fname = ".".join(os.path.basename(f).split(".")[:-1])
        if os.path.exists(f):
            if not os.path.isdir(f):
                with open(f) as fil:
                    Pythia.raw_files[fname] = fil.read()
            else:
                print "{} seems to be a directory.".format(f)
                if recurse == False:
                    return "Stopping..."
                elif recurse == True:
                    print "Recursively navingating directory {}".format(f)
                    addFiles(dir,*args)
                else:
                    recurse = raw_input("Recursively navigate through directory {}? (Y/n)".format(f))
                    if recurse[0].lower() == "n":
                        return "Stopping..."
                    else:
                        addFiles(dir,*args)
        else:
            print "Error: file or directory not found at {}".format(f)
    def addFiles(self,directory=None,*args):
        if directory:
            self._recursivelyOpen(directory)
        def argHandler(arg):
            if isinstance(arg,str):
                self._recursivelyOpen(arg)
            elif isinstance(arg,tuple):
                self.addFile(arg[0],arg[1])
            else:
                print "Warning: {} is not a valid argument...skipping..."
                pass
        for arg in args:
            if not isinstance(arg,(str,dict)):
                if len(arg) > 2:
                    for subArg in arg:
                        argHandler(subArg)
                else:
                    argHandler(arg)
            elif isinstance(arg,dict):
                for item in arg.items():
                    argHandler(item)
            else:
                argHandler(arg)
    def _recursivelyOpen(self,f):
        if os.path.isdir(f):
            l = [os.path.join(f,x) for x in os.listdir(f) if x[0] != "."]
            for x in l:
                _recursivelyOpen(x)
        else:
            addFile(f)

【问题讨论】:

  • 如果你import Pythia,那是一个模块,而不是一个类。所以你不能做Pythia()。你可以在模块中有一个名为Pythia 的类,也叫Pythia,在这种情况下你可以做Pythia.Pythia()。但是你最好通过调用模块 pythia 来避免混淆(正如 PEP8 建议的那样)。
  • 还有一件事:self.FileManger = FileManager() 是一个非常糟糕的主意。将一个类和该类的实例命名为相同的东西肯定会导致混淆。 (如果你按照 Eevee 的建议遵循 PEP8,这甚至永远不会出现 - file_manager 是通用 FileManager 实例的合理名称。但如果你不这样做,你需要一些其他风格来命名这些东西,比如 Apple-风格aFileManager.)

标签: python class inheritance


【解决方案1】:

首先:遵循PEP8 的指导方针。模块名、变量名、函数名应为lowercase_with_underscores;只有类名应该是CamelCase。否则,遵循您的代码有点困难。 :)

您在这里混淆了 OO 概念:您有一个包含子类实例的父类。

FileManager 是否主要做 Pythia 所做的工作,并进行一些修改或扩展?鉴于两者只能一起工作,我猜不会。

我不太确定您最终希望它是什么样子,但我认为您根本不需要继承。 FileManager 可以是它自己的类,Pythia 实例上的self.file_manager 可以是FileManager 的实例,然后Pythia 可以在必要时委托给它。这与您已经使用此代码的方式相去甚远。

构建小的独立部分,然后担心如何将它们相互插入。


此外,还有一些错误和样式问题:

  • 您拨打_recursivelyOpen(x),但忘记了self.。

  • 逗号后有一个空格。

  • 注意max 作为变量名:它也是内置函数的名称。

  • 如果可以,请避免类型检查 (isinstance)。当根据参数类型执行十几种不同的事情时,要遵循您的代码是非常困难的。具有非常明确的参数类型,并在必要时创建接受不同参数的辅助函数。

  • FileManager 内部有 Pythia.raw_files[fname],但 Pythia 是一个类,它没有 raw_files 属性。

  • 您检查recurse 是否为True,然后是False,然后... 其他。什么时候是别的东西?此外,您应该使用is 而不是== 来针对这样的内置单例进行测试。

【讨论】:

  • +1,除了你仍然错过了一个大问题:他在做import Pythia,然后是Pythia()。我不确定他是在寻找from Pythia import Pythia 代替第一个还是Pythia.Pythia() 代替第二个,您的建议将使区别变得明显——但他仍然需要以一种或另一种方式解决这个问题。
  • 噗,原来如此。不过,我认为这是假设的,而不是他尝试运行的实际代码。
  • 是的,他说这是他希望能够运行的代码。如果您阅读该段,他似乎将模块和类进一步混合在一起(例如,将FileManager 称为子模块)。
  • 我这里的代码是大量尝试在解释器中运行东西的结果,然后尝试修补/修改东西而没有非常清楚我在做什么......我尽量不要立即跑到 StackOverflow,以免我被真正知道发生了什么的人完全尴尬。我还应该承认,我没有遵守我的 PEP 规范……我被告知驼峰/下划线的区别是个人风格的问题。感谢您的提醒。
  • 这里有个秘密:我们并不总是知道发生了什么:)
【解决方案2】:

这里有很多内容,您最好多自学一些。

对于您的预期用途:

import Pythia
p = Pythia()
p.file_manager.addFile("/path/to/some/file")

这样的类结构可以工作:

class FileManager(object):
    def __init__(self, parent):
        self.parent = parent

    def addFile(self, file):
        # Your code
        self.parent.raw_files[file] = file

    def addFiles(self, files)
        # Your code
        for file in files:
            self.parent.raw_files[file] = file

class Pythia(object):
    def __init__(self):
        self.file_manager = FileManager(self)

但是有很多选择。您应该首先编写一些客户端代码来确定您想要什么,然后实现您的类/对象以匹配它。我从不倾向于在 python 中使用继承,由于 python 的鸭子类型,它并不是真正需要的。

此外,如果您希望在不实例化类的情况下调用方法,请使用静态方法,而不是类方法。例如:

class FileManager(object):
    @staticmethod
    def addFiles(files):
        pass

【讨论】:

  • 十分之九,@staticmethod 应该只是一个模块级函数。
  • @Eevee:+1。确切地。新手(尤其是最近的 Java 移民)的最佳经验法则是:使用 @classmethod 替代构造函数;永远不要使用@staticmethod。最终您会了解异常,但在您准备好确切了解它们的实际工作原理之前,经验法则已经足够好用了。
猜你喜欢
  • 2023-03-21
  • 2016-06-24
  • 2012-09-02
  • 1970-01-01
  • 2016-09-13
  • 1970-01-01
  • 2016-08-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多