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