Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    From string to constant to check type

    Cinema 4D SDK
    2
    6
    903
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • John_DoJ
      John_Do
      last edited by

      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,

      1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand
        last edited by ferdinand

        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 is eval 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 of eval 😉

        Cheers,
        Ferdinand

        import 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

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • ferdinandF
          ferdinand
          last edited by ferdinand

          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 use getattr(). Something like attached to the end of the posting.

          Cheers,
          Ferdinand

          import 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]
          

          MAXON SDK Specialist
          developers.maxon.net

          1 Reply Last reply Reply Quote 0
          • John_DoJ
            John_Do
            last edited by

            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 !

            1 Reply Last reply Reply Quote 0
            • ferdinandF
              ferdinand
              last edited by ferdinand

              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 as exec, 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

              MAXON SDK Specialist
              developers.maxon.net

              1 Reply Last reply Reply Quote 0
              • John_DoJ
                John_Do
                last edited by

                Thank you for the explanation @ferdinand.

                The example makes sense even for a beginner like me, that's great 🙂

                1 Reply Last reply Reply Quote 0
                • First post
                  Last post