Python:在定义类之前在模块中查找类

时间:2016-04-22 19:00:04

标签: python

我在模块中有一个python类,我有一些方法需要在同一个模块中有一些其他类的列表。以下是我现在正在做的事情:

module.py

class Main:
  @staticmethod
  def meth1():
    for c in classes:
      #do something

  @staticmethod
  def meth2():
    for c in classes:
      #do something

class Class1:
  pass

class Class2:
  pass

class Class3:
  pass

classes = [Class1, Class3]

我想改进的一些事情:

  1. 我想把classes列表放在更普遍的地方。理想情况下,在所有类之外,但在模块文件的顶部,或作为Main的类属性,但在meth1meth2之外。这样做的目的是为了更容易找到,如果有人需要添加另一个类定义。
  2. 如果可能的话,我想以编程方式执行此操作,因此我不需要明确定义列表。这消除了对#1的需求(尽管我仍然希望它很普遍)。为此,我需要一种方法来列出在同一模块中定义的所有类。我最接近的是dir()locals(),但它们也列出了导入的类,方法和模块。另外,我需要一些方法来识别我想要的类。我只能使用类中的属性来做到这一点,但如果有更优雅的方式,那就太好了。
  3. 我正在尝试做什么?

5 个答案:

答案 0 :(得分:4)

就个人而言,我会使用decorator来标记重要的类。您可以将将它们保存在文件顶部的列表放置在可以显示的位置。

这是一个简单的例子:

# Classes are added here if they are important, because...
important_classes = []


def important(cls):
    important_classes.append(cls)
    return cls


@important
class ClassA(object):
    pass


class ClassB(object):
    pass


@important
class ClassC(object):
    pass

# Now you can use the important_classes list however you like.
print(important_classes)
# => [<class '__main__.ClassA'>, <class '__main__.ClassC'>]

答案 1 :(得分:2)

您可以使用inspect。

首先,获取局部变量列表:

local_vars = locals().values()

然后我们需要检查每一个:

import inspect
local_vars = [i for i in local_vars if inspect.isclass(i)]

要仅获取本地定义的类,请检查cls.__module__ == __name__是否如下:

def get_classes():
    global_vars = list(globals().values())
    classes = [i for i in global_vars if inspect.isclass(i)]
    return [i for i in classes if i.__module__ == __name__]

总体思路是:inspect允许您检查实时对象,迭代所有局部变量允许您检查当前命名空间中的所有内容。最后一部分,即本地定义的类,可以通过检查模块名称是否与当前名称空间相同,或cls.__module__ == __name__来完成。

最后,为了兼容Python3,我添加了list(globals().values(),因为字典大小会在列表理解期间发生变化。对于Python2,由于dict.values()返回一个列表,因此可以省略。

编辑:

对于进一步过滤,您还可以使用特定的类属性或其他属性,如注释中所述。如果您担心稍后将模块重组为包,那就太棒了。

def get_classes(name='target'):
    global_vars = list(globals().values())
    classes = [i for i in global_vars if inspect.isclass(i)]
    return [i for i in classes if hasattr(i, name)]

答案 2 :(得分:2)

可能有更好的方法来实现这一点,但我会将所有这些子类放在持有者身上,然后使用__subclasses__()将它们全部拉出来:

class Main:
    def meth1(self):
        for c in Holder._subclasses__():
            #do something

    def meth2(self):
        for c in Holder._subclasses__():
            #do something

class Holder(object):
    pass

class Class1(Holder):
    pass

class Class2(Holder):
    pass

class Class3(Holder):
    pass

如果您愿意,甚至可以将它们设为Main的子类,然后使用类方法将它们拉出来:

class Main(object):
    @classmethod
    def meth1(cls):
        for c in cls._subclasses__():
            #do something

class Class1(Main): pass

您需要使用Python 2继承object才能实现此目的。

答案 3 :(得分:2)

您的列表似乎是针对模块中可用类的子集,因此在某些时候您必须指定要定位的类。

import sys

target_classes = ["Class1", "Class3"]

class Main:
    def __init__(self, classes):
        self.target_classes = classes

    def meth1(self):
        for s in self.target_classes:
            C = getattr(sys.modules[__name__], s)
            C().speak()

    def meth2(self):
        for c in classes:
            print c
            #do something

class Class1:
    def speak(self):
        print "woof"

class Class2:
    def speak(self):
        print "squeak"

class Class3:
    def speak(self):
        print "meow"


Main(target_classes).meth1()

--output:--
woof
meow

答案 4 :(得分:1)

我不确定这是否是最佳做法,但这可以满足您的需求:

class Main:
    def __init__(self, locals):
        self.classes = []
        for (c, val) in locals.iteritems():
            try:
                if c[:5] == 'Class':
                    self.classes.append(val)
            except:
                pass

    def meth1(self):
        for c in self.classes:
            pass

    def meth2(self):
        for c in self.classes:
            pass

class Class1:
    pass

class Class2:
    pass

class Class3:
    pass

main = Main(locals())
print main.classes