1

In Python there is no switch/case. It is suggested to use dictionaries: What is the Python equivalent for a case/switch statement?

in Python it is good practise to use @property to implement getter/setter: What's the pythonic way to use getters and setters?

So, if I want to build a class with a list of properties to switch so I can get or update values, I can use something like:

class Obj():                                                                                          
"""property demo"""                                                                                  

    @property                                                                                
    def uno(self):                                                                                              
        return self._uno                                                                                       
    @uno.setter                                                                                
    def uno(self, val):                                                                            
        self._uno = val*10                                                                                   

    @property                                                                                          
    def options(self):                                                                                        
        return dict(vars(self))   

But calling

o=Obj()
o.uno=10 # o.uno is now 100
o.options

I obtain {'_uno': 100} and not {'uno': 100}. Am I missing something?

leonard vertighel
  • 1,058
  • 1
  • 18
  • 37
  • 1
    Properties aren't included in `vars`, because they're not in the instance `__dict__`. You just see the backing attribute. But it's unclear to me what you mean by *"a list of properties to switch"*, so it's hard to say what the right implementation might be. – jonrsharpe Apr 17 '20 at 12:59

1 Answers1

2

vars is really a tool for introspection, and gives you the local variables of the current space, or in a given object - it is not a good way to get attributes and variables ready for final consumption.

So, your options code must be a bit more sophisticated - one way to go is to search the class for any properties, and then using getattr to get the values of those properties, but using the getter code, and introspect the instance variables, to get any methods attributed directly, but discard the ones starting with _:


    @property                                                                                          
    def options(self):            
        results = {}
        # search in all class attributes for properties, including superclasses:
        for name in dir(self.__class__):
            # obtain the object taht is associated with this name in the class
            attr = getattr(self.__class__, name)
            if isinstance(attr, property):   
                # ^ if you want to also retrieve other "property like"
                # attributes, it is better to check if it as the `__get__` method and is not callable:
                # "if hasattr(attr, '__get__') and not callable(attr):"

                # retrieves the attribute - ensuring the getter code is run:
                value = getattr(self, name)
                results[name] = value
        # check for the attributes assigned directly to the instance:
        for name, value in self.__dict__.items():
            # ^ here, vars(self) could have been used instead of self.__dict__ 
            if not name.startswith("_"):
                results[name] = value

        return results   

about switch..case

On a side note to your question, regarding the "switch...case" construction: please disregard all content you read saying "in Python one should use dictionaries instead of switch/case". This is incorrect.

The correct construct to replace "switch...case" in Python is the "if..elif..else". You can have all the expressiveness one does have with a C-like "switch" with a plain "if-else" tree in Python, and actually, go much beyond that, as the testing expression in if...elif can be arbitrary, and not just a matching value.

option = get_some_user_option()
if option == "A":
   ...
elif option == "B":
   ...
elif option in ("C", "D", "E"):
   # common code for C, D, E
   ...
   if option == "E":
        # specialized code for "E",  
else:
   # option does not exist.
   ...

While it is possible to use a dictionary as a call table, and having functions to perform actions in the dictionary values, this construct is obviously not a "drop in" replacement for a plain switch case - starting from the point that the "case" functions can't be written inline in the dictionary, unless they can be written as a lambda function, and mainly the point that they won't have direct access to the variables on the function calling them.

jsbueno
  • 99,910
  • 10
  • 151
  • 209