【问题标题】:Inverse of hasattr in PythonPython中hasattr的逆
【发布时间】:2013-04-09 04:51:36
【问题描述】:

hasattr(obj, attribute) 用于检查一个对象是否具有指定的属性,但是给定一个属性是否有办法知道它在哪里(全部)定义?

假设我的代码将属性(或类方法)的名称作为字符串获取,我想调用 classname.attribute 但我没有类名。

我想到的一个解决方案是这样的

def finder(attr):
    for obj in globals():
        try:
            if globals()[obj].__dict__[attr]:
                return(globals()[obj])
        except:
            ...

用法:

class Lime(object):
    @classmethod
    def lfunc(self):
        print('Classic')

getattr(finder('lfunc'),'lfunc')() #Runs lfunc method of Lime class

我很确定这不是最好的(甚至是正确的方法)。有人可以提供更好的方法吗?

【问题讨论】:

  • 情况似乎很糟糕。
  • 您为什么需要这样做?对于您要查找的函数,它们是否始终是在同一模块中定义的类的类方法?
  • 这不是hasattr()。反过来是not hasattr(obj, attr)。这只是一个等待被抛弃和重新思考的架构。你想解决什么问题?
  • 如果你能告诉我们你想通过这个实现的具体目标会更有用,因为你的想法是代码异味的明显标志,你可能会遗漏一些明显的东西。
  • 质疑投票者:问题详细说明了不应发生的代码情况这一事实不应使问题贬值 - 解释为什么这不是最好(甚至不是合理)的方式解决手头的问题应该来自 cmets 和答案。这个问题是很好定义和公式化的。如果对其他人没有用处,请不要点赞。

标签: python


【解决方案1】:

这可能会有所帮助:

import gc


def checker(checkee, maxdepth = 3):

    def onlyDict(ls):
        return filter(lambda x: isinstance(x, dict), ls)

    collection = []
    toBeInspected = {}
    tBI = toBeInspected
    gc.collect()
    for dic in onlyDict(gc.get_referrers(checkee)):
        for item, value in dic.iteritems():
            if value is checkee:
                collection.append(item)
            elif item != "checker":
                tBI[item] = value

    def _auxChecker(checkee, path, collection, checked, current, depth):
        if current in checked: return
        checked.append(current)
        gc.collect()
        for dic in onlyDict(gc.get_referents(current)):
            for item, value in dic.iteritems():
                currentPath = path + "." + item
                if value is checkee:
                    collection.append(currentPath)
                else:
                    try:
                        _auxChecker(checkee, currentPath, collection,
                                    checked, value, depth + 1)
                        if depth < maxdepth else None
                    except TypeError:
                        continue
    checked = []
    for item, value in tBI.iteritems():
        _auxChecker(checkee, item, collection, checked, value, 1)
    return collection

使用方法:

referrer = []

class Foo:        
        pass          

noo = Foo()       
bar = noo         
import xml        
import libxml2    
import sys        
import os         
op = os.path      
xml.foo = bar     
foobar = noo      

for x  in checker(foobar, 5):
    try:
        y= eval(x)
        referrer.append(x)
    except:
        continue

del x, y

ps:对于被检查者本身的递归或嵌套引用,将不会进一步检查被检查者的属性。

【讨论】:

  • 如果 OP wnats dto llok 的所有类都在当前模块(定义“检查器”的模块)的命名空间中,那么这将起作用。我认为这是最好的方法。
【解决方案2】:

它总是“可能的”。是否可取是另一段历史。

一种快速而肮脏的方法是对所有类进行线性迭代,并检查是否有任何定义了您拥有的属性。当然,这会产生冲突,它将产生第一个具有这样一个命名属性的类。如果它存在多个,则由您决定要哪个:

def finder(attr):
    for cls in object.__subclasses__(): 
         if hasattr(cls, attr):
              return cls
    raise ValueError

而不是在“globals”中搜索,而是搜索“object”的所有子类 - 因此要找到的类不需要位于 finder 函数所在模块的命名空间中。

但是,如果您的方法在您正在搜索的一组类中是唯一的,也许您可​​以组装所有方法的映射并使用它来调用它们。

假设您的所有类都来自一个名为“Base”的类:

mapper = {attr_name:getattr(cls, attr_name)  for cls in base.__subclasses__() for attr_name, obj in cls.__dict__.items()
             if isinstance(obj, classmethod) }

然后你用mapper['attrname']()给他们打电话

这避免了每次方法调用的线性搜索,因此会更好。

- 编辑-

__subclassess__ 只找到一个类的直接子类,而不是继承树 - 所以它在“现实生活”中没有用 - 也许它是在 OP 掌握的特定情况下。
如果需要在继承树中查找内容,则还需要递归每个子类。

至于旧式类:这当然行不通——这是在新代码中默认破坏它们的动机之一。

至于非类属性:无论如何只能在检查实例时找到它们 - 所以必须考虑另一种方法 - 这里似乎不是 O.P. 关心的问题。

【讨论】:

  • 这对于非常不雅的情况来说是不错的。谢谢jsbueno。
  • 由于重复(又名“冲突”)是可能的,我认为返回所有具有该属性的类的列表会更好——尽管这会使它 much 速度较慢,因为它无法在找到匹配项后立即退出。
  • 这是一个聪明的解决方案,但您还需要遍历找到的类的子类。它不会找到不从 object 继承的旧式 python 2.x 类,虽然它会找到类方法,但它不会找到不属于类模式的数据属性。
  • 我认为它不会找到数据属性这一事实也是一个重大限制。
  • 这不适用于子类的类方法,如 file 所示,它找不到 Derived 类的 method2()
【解决方案3】:

这应该适用于所有情况,但仍需要大量测试:

import inspect
import sys


def finder(attr, classes=None):
    result = []
    if classes is None:
        # get all accessible classes
        classes = [obj for name, obj in inspect.getmembers(
            sys.modules[__name__])]
    for a_class in classes:
        if inspect.isclass(a_class):
            if hasattr(a_class, attr):
                result.append(a_class)
            else:
                # we check for instance attributes
                if hasattr(a_class(), attr):
                    result.append(a_class)
            try:
                result += finder(attr, a_class.__subclasses__())
            except:
                # old style classes (that don't inherit from object) do not
                # have __subclasses; not the best solution though
                pass
    return list(set(result))  # workaround duplicates


def main(attr):
    print finder(attr)
    return 0


if __name__ == "__main__":
    sys.exit(main("some_attr"))

【讨论】:

  • 非常感谢。我试试看
猜你喜欢
  • 2014-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-25
  • 2018-07-23
  • 2010-11-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多