我们是否可以找到一个对象的_local_类名,该对象是从导入为'导入的类实例化的'?

时间:2014-10-27 02:52:24

标签: python python-import

如果我导入一个类并通过子类重命名它,找到新的类名很简单:

>>> from timeit import Timer
>>> class Test(Timer):
...     pass
... 
>>> test = Test()
>>> test.__class__.__name__
'Test'

但是,如果我在导入它时对该类进行别名,它会保留其主机模块中的名称:

>>> from timeit import Timer as Test2
>>> test2 = Test2()
>>> test2.__class__.__name__
'Timer'

稍后,我想提供面向用户的输出,该输出知道他们在命名空间中给出了该类的名称。考虑:

def report_stats(timer):
    print("Runtime statistics for %s:" % timer.__class__.__name__)
    ...

有没有办法获得字符串读取" Test2",没有迭代命名空间中的变量来测试完全匹配?

1 个答案:

答案 0 :(得分:0)

对我自己的问题有一个非常可怕的答案;我不会接受这个,因为它可能非常脆弱(我只测试了一组有限的通话情况)。我大多只是为了挑战而追捕这个;我很可能会在实际使用案例中使用更耐用的东西。

这假设我们可以访问我们尝试导入为blah的类的 init 函数,以及某种持久的外部数据存储,至少对于更复杂的边缘情况:

import inspect, dis

class Idiom(object):
    description = None
    alias = None

    def __init__(self, desc):
        global data_ob
        self.description = desc

        if self.__class__.__name__ == 'Idiom':
            #cheat like hell to figure out who called us
            self.alias = data_ob.name_idiom(inspect.currentframe().f_back)
        else:
            self.alias = self.__class__.__name__

class DataOb(object):
    code = None
    locations = {}
    LOAD_NAME = 101
    codelen = None

    def name_idiom(self, frame):
        if not self.code:
            self.code = frame.f_code
            self.codelen = len(self.code.co_code)
            self.locations = {y:x for x, y in dis.findlinestarts(self.code)}

        target_line = frame.f_lineno
        addr_index = self.locations[target_line]+1
        name_index = self.code.co_code[addr_index]

        # there's a chance we'll get called again this line,
        # so we want to seek to the next LOAD_NAME instance(101)
        addr_index += 1
        while addr_index < self.codelen:
            if self.code.co_code[addr_index] == self.LOAD_NAME:
                self.locations[target_line] = addr_index
                break
            addr_index += 1

        return self.code.co_names[name_index]

关于其工作原理的简短说明是:

  • 我们从init函数
  • 中查找上一帧
  • 获取代码对象
  • 找到代码中每行开头的字节码位置
  • 使用帧中的行号来获取该行开头的字节码位置
  • 在此行的字节码中找到一个LOAD_NAME指标(我并非真的遵循此规则;我的代码假定它会在那里)
  • 查看索引的下一个字节码位置,该索引指示code.co_names元组中哪个位置包含&#34; name&#34; LOAD_NAME致电

从这里我们可以做类似的事情:

>>> from rabbit_hole import Idiom as timer_bob
>>> with timer_bob("down the rabbit hole"):
...     waste_some_time = list(range(50000))
... 
timer_bob: down the rabbit hole
    runtime: 0:00:00.001909, children: 0:00:00, overhead: 0:00:00.001909