如何检查实例化的类属性类型?

时间:2017-11-30 22:55:36

标签: python object types attributes instance

我正在尝试检查我实例化的对象是否基本上实例化了子对象,这样我就可以继续这种方式,直到我完成检查/错误检查输入/填充我的对象。目标是填充该对象的信息,而不知道属性是什么,因为它们是由其他代码自动生成的,因此它们如有更改,恕不另行通知。我正在尝试使用递归函数。一个例子可能会更清楚一些:

### content of B.py:
class B:
    def __init__(self):
        self.Ba = ''
        self.Bb = 0


### content of A.py
from B import B

class A:
    def __init__(self):
        self.B = B()
        self.Aa = ''
        self.Ab = False


### content of SDK.py
from A import A
primitiveTypes = (int, str, bool, type(None))

class G:
    def createIt(self, **options)
        fillMe = A()
        fillMe = self.fillWithData(fillMe, options)
        #Do stuff with fillMe (post in JSON format to REST API...)

    def fillWithData(self, obj, options)
        for a in dir(obj):
            if not callable(a) and not a.startswith('_'):
                if isinstance(a, primitiveTypes):
                    obj.__dict__.update({a: options.get(a)})
                else:
                    #This never gets executed because isinstance always returns true -> because it evaluates A.B as type string instead of being of type B
                    obj.__dict__.update({a: self.fillWithData(a, options)})

### content of test.py
from SDK import G

g = G()
g.createIt(Aa='Aa', Ab=True, Ba='Ba', Bb=1) #Make correct input succeed
g.createIt(Aa=1, Ab='', Ba=None, Bb=False) #Make incorrect input fail
编辑:我删除了退货声明,谢谢你清除我的疑虑。

解释为什么我需要所有这些不同的文件;我目前正在编写一个SDK,以便人们可以使用它与REST API进行交互(创建用户,登录,添加内容,修改内容,删除内容,比如角色,权限等)。所以我们有代码在C#定义模型和枚举(无方法),由代码生成器转换为python。在上面的例子中,模型将是A.py和B.py.我将它们导入SDK(G.py),因此我有test.py来使用SDK方法。

所以问题是:isinstance总是返回true - 因为它认为对象b的类型为String时它应该是B类型。

2 个答案:

答案 0 :(得分:1)

所以,有几点。首先,而不是制作"原始类型的元组" (注意,Python 没有原始类型)枚举你想要处理的类型更有意义,所以:

In [1]: class B:
   ...:     def __init__(self):
   ...:         self.Ba = ''
   ...:         self.Bb = 0
   ...:

In [2]: class A:
   ...:     def __init__(self):
   ...:         self.B = B()
   ...:         self.Aa = ''
   ...:         self.Ab = False
   ...:

In [3]: registered_types = A, B

其次,不需要课程G,它不会保持任何状态,只会使事情复杂化。如果您的实际用例可以通过成为一个类来改进,请继续进行调整,但为了清晰起见,我将仅使用一个普通函数:

In [5]: SENTINEL = object()

In [6]: def initialize(obj, **options):
    ...:     for name, subobj in vars(obj).items():
    ...:         if isinstance(subobj, registered_types):
    ...:             filled = initialize(subobj, **options)
    ...:             setattr(obj, name, filled)
    ...:         else:
    ...:             val = options.get(name, SENTINEL)
    ...:             if val is not SENTINEL:
    ...:                 setattr(obj, name, val)
    ...:     return obj
    ...:

注意,我使用vars来检查obj的实例属性,所有vars(obj)都返回obj.__dict__,这是你想要的,而不是检查if x.startswith('_')等等dir返回的内容(实际上并不是用于生产,而是用于调试)。此外,我迭代vars,因为那(和dir)返回一个dict对象,并直接迭代它迭代键,它总是 strings ,这是你以前处理的。另请注意,我创建了一个任意SENTINEL对象,只是为了检查而不是None,这可能是一个有效的目标。另外,我使用setattr代替较粗略的obj.__dict__.update(...)构建。

最后,结果:

In [7]: a = A()

In [8]: a = initialize(a, Aa='Aa', Ab=True, Ba='Ba', Bb=1)

In [9]: a.Aa, a.Ab, a.B.Ba, a.B.Bb
Out[9]: ('Aa', True, 'Ba', 1)

答案 1 :(得分:0)

谢谢@ juanpa.arrivillaga,解决这个问题的关键是真正使用价值而不是关键(听起来如此明显,现在就这样说道了)以下是我们最终解决它的方式:

def fillWithData(self, obj, options):
keysToDelete = []
for name, subObj in vars(obj).items():
    if isinstance(subObj, primitiveTypes):
        obj.__dict__.update({name: options.get(name)})
        if obj.__dict__[name] is None: #To remove keys with empty values later
            keysToDelete.append(name)
    elif not isinstance(subObj, types.FunctionType) and not isinstance(subObj, Enum) and not name.startswith('_'):
        obj.__dict__.update({name: self.fillData(subObj, options)})
for k in keysToDelete: #Remove keys with empty values
    del obj.__dict__[k]
return obj

下一步...清理这个东西所以我实际上并没有用空值填充对象,然后删除它们;;) - >也许在未来的另一个问题中hehe:P