简体   繁体   English

Python:如何确定属性(按名称)是类还是实例属性?

[英]Python: How determine if attribute (by name) is class or instance attribute?

Goal (in Python 2.7): 目标(在Python 2.7中):

Inspecting an arbitrary object, find all of the instance variables. 检查任意对象,找到所有实例变量。 But exclude class variables. 但排除类变量。

Ultimate goal: 最终目标:

Print useful details of an object, from a third-party class library that doesn't provide a useful "str" implementation. 从未提供有用的“ str”实现的第三方类库中打印对象的有用细节。 (Maya's Python API, version 1, which is a simple SWIG wrapper. not using version 2, because I'm learning from some version 1 examples.) (Maya的Python API版本1是一个简单的SWIG包装器。不使用版本2,因为我正在学习一些版本1的示例。)

Example class: 示例类:

# ---------- class Vector ----------
class Vector(object):
    def __init__(self, x=0.0, y=0.0, z=0.0):
        self.x, self.y, self.z = x, y, z
    # Provide useful info for 'repr(self)', 'str(self)', and 'print self'.
    def __repr__(self):
        return 'Vector({0}, {1}, {2})'.format(self.x, self.y, self.z)
    # math operators
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y, self.z + other.z)
    # a simple method
    def ApproximateLength(self):
        return self.x + self.y + self.z
    # list/sequence/iterator support.
    def tolist(self):
        return [self.x, self.y, self.z]
    def __len__(self):
        return 3
        # No need for "next(self)", because we create a list, use its iterator.
    def __iter__(self):
        return iter(self.tolist())
# class variable
Vector.Zero = Vector()

Solution so far: 到目前为止的解决方案:

import inspect
import types
def printElements(ob):
    for x in ob: print x
# Excludes 'internal' names (start with '__').
def Public(name):
    return not name.startswith('__')
def Attributes(ob):
    # Exclude methods.
    attributes = inspect.getmembers(ob, lambda member: not inspect.ismethod(member))
    # Exclude 'internal' names.
    publicAttributes = filter(lambda desc: Public(desc[0]), attributes)
    return publicAttributes

Example usage: 用法示例:

vec = Vector(1.0, 2.0, 3.0)
printElements(Attributes(vec))   

Output: 输出:

('Zero', Vector(0.0, 0.0, 0.0))
('x', 1.0)
('y', 2.0)
('z', 3.0)

This class does print itself well: 该类的打印效果很好:

print vec

=> =>

Vector(1.0, 2.0, 3.0)

The goal is to extract similar information, for classes that I don't have source to (or don't want to modify the source of). 目的是为我没有来源(或不想修改其来源)的类提取类似的信息。 Those classes have many class variables, which bury the information I seek. 这些类具有许多类变量,这些变量掩藏了我寻求的信息。

Question: 题:

How detect that 'Zero' is a "class variable", inherited from Vector, to eliminate it from the output? 如何检测“零”是从Vector继承的“类变量”,以将其从输出中消除?

Clumsy approach I will use if no better way: 如果没有更好的方法,我将使用笨拙的方法:

printElements(Attributes(type(vec)))

lists the attributes on the object's type. 列出对象类型的属性。 Could test each attribute of "vec" against the attributes of "type(vec)", excluding any that match. 可以对照“ type(vec)”的属性测试“ vec”的每个属性,不包括任何匹配的属性。 I don't care about the subtle possibility that the same named attribute exists on both class and instance. 我不在乎在类和实例上都存在相同的命名属性的细微可能性。 So this would satisfy my requirements. 因此,这将满足我的要求。

However, that seems clumsy. 但是,这似乎很笨拙。 Is there a more direct way to determine whether the attribute is inherited from the class? 有没有更直接的方法来确定属性是否从类继承?


EDIT: Incorporating Joran's answer : 编辑:结合乔兰的答案

def IsClassVar(self, attrName):
    return hasattr(self.__class__, attrName)
def Attributes(ob):
    ....
    publicAttributes = filter(lambda desc: Public(desc[0]), attributes)
    # Exclude 'class' variables.
    # NOTE: This does not attempt to detect whether the instance variable is different than the class variable.
    publicAttributes = filter(lambda desc: not isClassVar(ob, desc[0]), publicAttributes)
    return publicAttributes

This gives the desired result: 这样可以得到预期的结果:

printElements(Attributes(vec))   

=> =>

('x', 1.0)
('y', 2.0)
('z', 3.0)

Alternative, To detect instance variable overriding class variable: 或者,要检测实例变量重写类变量,请执行以下操作:

def IsClassVar(self, attrName):
    return hasattr(self.__class__, attrName)
# REQUIRE attrName already known to be supported by self.
# But just in case, return False if exception, so will be skipped.
def IsNotSameAsClassVar(self, attrName):
    try:
        if not IsClassVar(self, attrName):
            return True
        # If it has different value than class' attribute, it is on the instance.
        return getattr(self, attrName) is not getattr(self.__class__, attrName)
    except:
        return False
def Attributes(ob):
    ....
    publicAttributes = filter(lambda desc: Public(desc[0]), attributes)
    # Exclude 'class' variables.
    # More complete solution.
    publicAttributes = filter(lambda desc: IsNotSameAsClassVar(ob, desc[0]), publicAttributes)
    return publicAttributes

Now if we override 'Zero' on vec, it gets included: 现在,如果我们在vec上覆盖“零”,它将包括在内:

# Probably a bad idea, but showing the principle.
vec.Zero = "Surprise!"

Then: 然后:

print vec.Zero
print Vector.Zero

=> =>

Surprise!
Vector(0.0, 0.0, 0.0)

And: 和:

printElements(Attributes(vec))   

=> =>

('Zero', 'Surprise!')
('x', 1.0)
('y', 2.0)
('z', 3.0)

something like this may work 这样的事情可能会起作用

def isClassVar(self,varname):
        return hasattr(self.__class__,varname)
...
vec.isClassVar("Zero")

note that this does not necessarily mean it is an instance variable ... just that is is not a class variable 请注意,这并不一定意味着它是一个实例变量……只是这不是一个类变量

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM