Return all class variable values from a Python Class
Asked Answered
R

4

5

I have a Python class that contains all the AWS regions. I wrote a class method that would return me the list of all the regions. Is there a better way of returning all the class variable values so I don't have to hard-code all the values in the return statement like I am doing in the example below?

class AwsRegion():
    '''
    Class to define AWS Regions
    '''
    OHIO = 'us-east-2'
    NORTH_VIRGINIA = 'us-east-1'
    NORTH_CALIFORNIA = 'us-west-1'
    OREGON = 'us-west-2'
    MUMBAI = 'ap-south-1'
    SEOUL = 'ap-northeast-2'
    SINGAPORE = 'ap-southeast-1'
    SYDNEY = 'ap-southeast-2'
    TOKYO = 'ap-northeast-1'
    FRANKFURT = 'eu-central-1'
    IRELAND = 'eu-west-1'
    LONDON = 'eu-west-2'
    SAO_PAULO = 'sa-east-1'

    @classmethod
    def all(cls, ):
        return [AwsRegion.OHIO, AwsRegion.NORTH_VIRGINIA, AwsRegion.NORTH_CALIFORNIA, AwsRegion.OREGON, \
            AwsRegion.MUMBAI, AwsRegion.SEOUL, AwsRegion.SINGAPORE, AwsRegion.SYDNEY, AwsRegion.TOKYO, \
            AwsRegion.FRANKFURT, AwsRegion.IRELAND, AwsRegion.LONDON, AwsRegion.SAO_PAULO]
Rangoon answered 27/6, 2017 at 21:15 Comment(2)
all is a class attribute too. Do you mean anything that isn't a callable or otherwise a descriptor?Engvall
There must be a simpler way to return those values than hardcoding them in a list...Orchestral
E
5

In this case, you can enumerate all the attributes of the class that are uppercase; I'd use the vars() function to access the class namespace:

@classmethod
def all(cls):
    return [value for name, value in vars(cls).items() if name.isupper()]

Demo:

>>> class AwsRegion():
...     '''
...     Class to define AWS Regions
...     '''
...     OHIO = 'us-east-2'
...     NORTH_VIRGINIA = 'us-east-1'
...     NORTH_CALIFORNIA = 'us-west-1'
...     OREGON = 'us-west-2'
...     MUMBAI = 'ap-south-1'
...     SEOUL = 'ap-northeast-2'
...     SINGAPORE = 'ap-southeast-1'
...     SYDNEY = 'ap-southeast-2'
...     TOKYO = 'ap-northeast-1'
...     FRANKFURT = 'eu-central-1'
...     IRELAND = 'eu-west-1'
...     LONDON = 'eu-west-2'
...     SAO_PAULO = 'sa-east-1'
...     @classmethod
...     def all(cls):
...         return [value for name, value in vars(cls).items() if name.isupper()]
...
>>> AwsRegion.all()
['us-east-2', 'us-east-1', 'us-west-1', 'us-west-2', 'ap-south-1', 'ap-northeast-2', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'eu-central-1', 'eu-west-1', 'eu-west-2', 'sa-east-1']
Engvall answered 27/6, 2017 at 21:17 Comment(3)
Is this recommended, considering class method is not returning an instance of class?Somersomers
@Sajal: classmethods can return anything, they are not required to return a class. That's not what a classmethod is about. Classmethods are simply bound to the class they are accessed on, or the class of an instance they are accessed on. One of their use cases is to return class-specific information, like all the uppercase attributes here.Engvall
Oh cool! While checking for class methods on GFG, there it was mentioned that return type is class instance (modified), that's why needed to confirm, if this is a safe practice. Thanks!Somersomers
C
6

As a general reference, you can get the attributes of any class through the following ways.
Choose depending on your needs:

__dict__:

Will return a dict of all writeable class attributes. This is usually what you need.

my_obj = MyClass()
attributes = my_obj.__dict__

vars():

Same result as __dict__, but using this instead is considered best practice.

my_obj = MyClass()
attributes = vars(my_obj)

dir():

Will return all class attributes, including those that aren't made by you but are inherited from object.

my_obj = MyClass()
attributes = dir(my_obj)

In your case, using vars() will work just fine, as demonstrated by Martijn Pieters in his answer.

Cocteau answered 27/6, 2017 at 21:18 Comment(1)
You mentioned vars() but typed dir(myObj) in this line: vars = dir(myObj). Is that a typo?Rangoon
E
5

In this case, you can enumerate all the attributes of the class that are uppercase; I'd use the vars() function to access the class namespace:

@classmethod
def all(cls):
    return [value for name, value in vars(cls).items() if name.isupper()]

Demo:

>>> class AwsRegion():
...     '''
...     Class to define AWS Regions
...     '''
...     OHIO = 'us-east-2'
...     NORTH_VIRGINIA = 'us-east-1'
...     NORTH_CALIFORNIA = 'us-west-1'
...     OREGON = 'us-west-2'
...     MUMBAI = 'ap-south-1'
...     SEOUL = 'ap-northeast-2'
...     SINGAPORE = 'ap-southeast-1'
...     SYDNEY = 'ap-southeast-2'
...     TOKYO = 'ap-northeast-1'
...     FRANKFURT = 'eu-central-1'
...     IRELAND = 'eu-west-1'
...     LONDON = 'eu-west-2'
...     SAO_PAULO = 'sa-east-1'
...     @classmethod
...     def all(cls):
...         return [value for name, value in vars(cls).items() if name.isupper()]
...
>>> AwsRegion.all()
['us-east-2', 'us-east-1', 'us-west-1', 'us-west-2', 'ap-south-1', 'ap-northeast-2', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'eu-central-1', 'eu-west-1', 'eu-west-2', 'sa-east-1']
Engvall answered 27/6, 2017 at 21:17 Comment(3)
Is this recommended, considering class method is not returning an instance of class?Somersomers
@Sajal: classmethods can return anything, they are not required to return a class. That's not what a classmethod is about. Classmethods are simply bound to the class they are accessed on, or the class of an instance they are accessed on. One of their use cases is to return class-specific information, like all the uppercase attributes here.Engvall
Oh cool! While checking for class methods on GFG, there it was mentioned that return type is class instance (modified), that's why needed to confirm, if this is a safe practice. Thanks!Somersomers
S
1

It looks like what you might be doing is accidentally creating an Enum type.

If your AwsRegion class is only used to store these values (not lots of other complex behavior), try making it a subclass of Enum. This would give you a handful of nice methods without having to recreate it all yourself and would make your code clearer to others who know what enumerate types are.

from enum import Enum

class AwsRegion2(Enum):
    OHIO = 'us-east-2'
    NORTH_VIRGINIA = 'us-east-1'
    NORTH_CALIFORNIA = 'us-west-1'
    OREGON = 'us-west-2'
    MUMBAI = 'ap-south-1'
    SEOUL = 'ap-northeast-2'
    SINGAPORE = 'ap-southeast-1'
    SYDNEY = 'ap-southeast-2'
    TOKYO = 'ap-northeast-1'
    FRANKFURT = 'eu-central-1'
    IRELAND = 'eu-west-1'
    LONDON = 'eu-west-2'
    SAO_PAULO = 'sa-east-1'


print(list(AwsRegion2))
Subjective answered 27/6, 2017 at 22:25 Comment(0)
Q
1

From the combination of the mentioned answers, I think this is more efficient:

from enum import Enum

class AwsRegion2(Enum):
    OHIO = 'us-east-2'
    NORTH_VIRGINIA = 'us-east-1'
    NORTH_CALIFORNIA = 'us-west-1'
    OREGON = 'us-west-2'
    MUMBAI = 'ap-south-1'
    SEOUL = 'ap-northeast-2'
    SINGAPORE = 'ap-southeast-1'
    SYDNEY = 'ap-southeast-2'
    TOKYO = 'ap-northeast-1'
    FRANKFURT = 'eu-central-1'
    IRELAND = 'eu-west-1'
    LONDON = 'eu-west-2'
    SAO_PAULO = 'sa-east-1'

    @classmethod
    def all(cls):
        return [variable.value for variable in list(cls)]

print(AwsRegion2.all())
Quality answered 8/9, 2021 at 11:18 Comment(1)
The cleanest solution so far it seems. ThanksCheliform

© 2022 - 2024 — McMap. All rights reserved.