From string to constant to check type
-
Hello !
I've wrote a simple function to store some constants in a list, from a string ( because I'm lazy) :
def geoTypeList(): geo_constants = "tube,cube,plane" # not the full list geo_types = [("c4d.O" + i) for i in geo_constants.split(",")] return geo_types # return the list
I was happy with my solution until I've tried to use it :
for i in objs: if i.GetType() in geoTypeList(): print(i.GetName(), "is a", i.GetTypeName(), i.GetRealType()) else: print("Not a geometry object")
I've tried many times and many variant until I've realized that I'm trying to check a constant against some strings, so it can't work. But I'm stuck here, I don't know how to pass the list items as constants and not as string.
Also before you ask, why I'm not just using ID integers ? Because I would like to keep the code easily readable, and the docs use the constants and symbols.Thank you,
-
Hi @John_Do,
thank you for reaching out to us. You could use Python's
eval
to evaluate the string you do build. Although I do not quite see the point in doing it, since you open the can of worms that iseval
with it (if something goes south, it could do do bad things). So I would strongly recommend not to try something like this (although the chances of something going south here are probably slim). Consider yourself warned about the pitfalls ofeval
Cheers,
Ferdinandimport c4d def geoTypeList(): geo_constants = "tube,cube,plane" # not the full list # Use eval to store the integer repr and not a string repr of the symbol geo_types = [eval("c4d.O" + i) for i in geo_constants.split(",")] return geo_types # return the list nodes = [c4d.BaseList2D(c4d.Ocube), c4d.BaseList2D(c4d.Otube), c4d.BaseList2D(c4d.Oplane), c4d.BaseList2D(c4d.Osphere)] for i in nodes: if i.GetType() in geoTypeList(): print(i.GetName(), "is a", i.GetTypeName(), i.GetRealType()) else: print(f"Not a geometry object: {i}") # Although I do not quite see the point in doing it like that. This seems to # be much more convenient and more importantly less dangerous: typeSymbols = (c4d.Otube, c4d.Ocube, c4d.Oplane) for i in nodes: if i.GetType() in typeSymbols: print(i.GetName(), "is a", i.GetTypeName(), i.GetRealType()) else: print(f"Not a geometry object: {i}")
edit: Here is some context about the pitfalls of
eval
: https://realpython.com/python-eval-function/#minimizing-the-security-issues-of-eval -
Hi @John_Do,
my solution was not very good due to the inherent diceyness of
eavl
. Since you are only interested in an attribute, you could of course usegetattr()
. Something like attached to the end of the posting.Cheers,
Ferdinandimport c4d def yieldSymbolsFromString(symbolString): """Just gets the attributes from the module. """ for s in symbolString.split(","): val = getattr(c4d, f"O{s}") yield val nodes = [c4d.BaseList2D(c4d.Ocube), c4d.BaseList2D(c4d.Otube), c4d.BaseList2D(c4d.Oplane), c4d.BaseList2D(c4d.Osphere)] for node in nodes: if node.GetType() in yieldSymbolsFromString("tube,cube,plane"): print(f"{node.GetName()} is a geometry.") else: print(f"{node.GetName()} is not a geometry.") # Will raise an attribute error on Ocar. for s in yieldSymbolsFromString("tube,cube,plane,car"): pass
Cube is a geometry. Tube is a geometry. Plane is a geometry. Sphere is not a geometry. Traceback (most recent call last): File "...\scribbles_a.py", line 22, in <module> for s in yieldSymbolsFromString("tube,cube,plane,car"): File "...\scribbles_a.py", line 7, in yieldSymbolsFromString val = getattr(c4d, f"O{s}") AttributeError: module 'c4d' has no attribute 'Ocar' [Finished in 4.1s]
-
Hi @ferdinand,
In fact I think I was looking for the eval() method which I know but somewhat have forgotten.
Thanks for pointing the fact that using it could be dangerous, but in this particular context I'm not sure to understand why ? I'm not a seasoned coder ( far from it ) but the linked article seems to point security issues in the context of using eval() in conjunction with input(), which is not the case here.
I'm doing this mainly to practice Python as much as possible, but I guess it is wasted computing power since these symbols are not changing anytime soon.
Anyway, I've learned some things, thank you for the help, much appreciated !
-
Hi @John_Do,
the reason why
eval
is bad is because you always have to sanitize your inputs (in your case the string). Which might sound trivial to do, but is actually hard when you have to guarantee that nothing can go wrong.eval
is of course not as bad asexec
,runpy
or similar things, but this whole idea of cobbeling code together at runtime often produces more problems than it solves. It can of course be a powerful tool, but there are often more "boring" and safer alternatives.If then something goes wrong, finding the bug is usually hard, because you have then to untangle the code created dynamically at runtime. In this case problems are unlikely as I said, but something like this could happen for example:
import c4d code = "c4d.Ocone, c4d.Ocube + c4d.Osphere" for token in code.split(","): print(f"{token.strip()}: {eval(token)}")
c4d.Ocone: 5162 c4d.Ocube + c4d.Osphere: 10319
So there is nothing which prevents you from accidentally adding two symbols together. Here it is easy to spot, but just hide that bit in 5k lines of code and dynamically build that
code
string at runtime and it becomes a nightmare to debug. So I would consider this bad practice which is why I was so verbal about my warnings.getattr
for example is a much better solution for your problem, because it does not come with these problems.Cheers,
Ferdinand -
Thank you for the explanation @ferdinand.
The example makes sense even for a beginner like me, that's great