1

The approach might be just wrong to begin with, but I'm trying to do the following:

class Material:
    pass


class Vacuum(Material):
    def __str__(self):
        return 'vacuum'


class Aluminum(Material):
    def __str__(self):
        return 'aluminum'


class Graphite(Material):
    def __str__(self):
        return 'graphite'


class Beryllium(Material):
    def __str__(self):
        return 'beryllium'

I have different pieces of code that deals with different materials. Instead of passing a string as argument to that other pieces I would prefer to give it objects. This allows to have tab-completion with ipython and it is also a way to enforce the type.

To avoid changing the already written pieces, those will just do str(argument): if it is a string it recovers the old behavior, if it is one of the objects it will work.

The question is now: I want to support a given list of materials:

allowed_materials = ['vacuum', 'aluminum', 'graphite',]

and that list might be growing. Instead of manually writing the classes, how could I generate them based on the list?

2
  • do you need different classes for all materials ? or the name could be an attribute of a class Material ? the class can return this attribute in __str__ Commented Jul 21, 2018 at 13:25
  • @PRMoureu Agreed, I also thought about it but in the end the classes will do more than just that. Commented Jul 21, 2018 at 13:49

3 Answers 3

1

You can define a metaclass that can generate your classes for you.

class mattype(type):
    def __new__(mcls, name, bases=(), d=None):
        def __str__(self):
            return name.lower()
        if not d:
            d = {}
        d['__str__'] = __str__
        bases = (*bases, Material)
        return super().__new__(mcls, name.title(), bases, d)

allowed_materials = ['vacuum', 'aluminum', 'graphite',]
classes = {name: mattype(name) for name in allowed_materials}
str(classes['vacuum']())
# 'vacuum'
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. I have to see how that compares to what I'm doing.
1

If you do not need different class name for different material you can simply initialise it inside the material class. If not I will delete my answer.

class Material:
    def __init__(self,name):
        self.name=name
    def __str__(self):
        return self.name

allowed_materials = ['vacuum', 'aluminum', 'graphite',]

obj_lst=[Material(material) for material in allowed_materials]

for obj in obj_lst:
    print(str(obj))

output:

vacuum
aluminum 
graphite

Comments

1

I ended up doing the following, also adding objects to the module.

import sys


class Material:
    def __str__(self):
        return self.__class__.__name__

    pass


print(sys.modules[__name__])

_materials = ['Copper', 'Vacuum']

for m in _materials:
    setattr(sys.modules[__name__], m, type(m, (Material,), {})())

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.