So in python, there's the getattr function to automate getting attributes of a particular class, fair enough.
What I am trying to do is create a class from a user input, similar to how I could loop through class attributes using getattr.
Here's the process that I am sort of thinking of:
# Define classes that the user could chose
class Person():
def __init__(self):
self.prop1 = 'hi'
# Define other classes
className = input() # The user would then type a class name like Person
newObj = className() # I would want this to evaluate like Person()
Basically, I'm trying to not make my code look like a bunch of
if className == "Person":
newObj = Person()
elif className == "Dog":
newObj = Dog()
Basically, is there any built in function where I can refer to a class through a string?
If both classes' __init__ take the same arguments, the simplest way is with a mapping dict. My example below involves a cheeky error handling, which you can avoid if you split the second line into several.
mapping_dict = {'Person': Person, 'Dog': Dog}
instance = mapping_dict.get(user_input, lambda: print('Invalid class name'))()
# instance is None if an invalid input was provided
Without the cheeky error handling:
mapping_dict = {'Person': Person, 'Dog': Dog}
class_obj = mapping_dict.get(user_input)
if class_obj:
instance = class_obj()
else:
print('Invalid class name')
You can use factory design pattern:
class CAR(object):
def __str__(self):
return "This is a car"
class BUS(object):
def __str__(self):
return "This is a bus"
class TRAIN(object):
def __str__(self):
return "This is a train"
def create_vehicle(vehicle_type):
target_class = vehicle_type.upper()
return globals()[target_class]()
vehicles = ['bus', 'car', 'train']
for v in vehicles:
print (create_vehicle(v))
Output would be :
This is a bus
This is a car
This is a train
There is another metaclass based solution, though, it might be overkill for this simple requirement.
_registry = {}
class MetaClass(type):
def __init__(cls, clsname, bases, methods):
super().__init__(clsname, bases, methods)
_registry[cls.__name__] = cls
class CAR(metaclass=MetaClass):
def __str__(self):
return "This is a car"
class BUS(metaclass=MetaClass):
def __str__(self):
return "This is a bus"
class TRAIN(metaclass=MetaClass):
def __str__(self):
return "This is a train"
def create_vehicle(vehicle_type):
target_class = vehicle_type.upper()
return _registry[target_class]()
vehicles = ['bus', 'car', 'train']
for v in vehicles:
print (create_vehicle(v))
A dynamic approach below
import sys, inspect
def get_classes():
return {name: obj for name, obj in inspect.getmembers(sys.modules[__name__]) if inspect.isclass(obj)}
class Person():
def __init__(self):
print('Person ..')
self.prop1 = 'hi 0'
class Student():
def __init__(self):
print('Student ..')
self.prop1 = 'hi 1'
classes_mapping = get_classes()
user_input = input('Type class name:')
clazz = classes_mapping.get(user_input)
if clazz:
obj = clazz()
else:
print(f'Unknown class {user_input}')
Related
I have a list of objects of the Person class. This list includes myself, so I need to remove "myself" from the list.
It means I need to remove the object from the list that calls this method.
class Population:
def __init__(self):
self.people = list()
class Person:
def __init__(self):
self.friends_list = list()
def make_friends(self, population, n):
temp_list = population.copy()
temp_list.remove(self)
self.friends_list.extend(random.sample(temp_list,n))
my test:
per = Person()
per2 = Person()
per3 = Person()
per4 = Person()
per5 = Person()
pop = [per,per2,per3,per4,per5]
for per in pop:
per.make_friends(pop, 2)
print('ME: ',per)
print(per.friends_list)
My tests run well, but there are general tests that check the code and they generate an error on this line:
try:
stud_res = person.make_friends(population, count)
except Exception:
print("\tPerson.make_friends() generated error")
return
Can I use self in this way, and if not, how can I better remove "myself" from the list?
It is a perfectly fine use case. By the way, note that you're overriding the builtin list.
To use self, you have to share a list collection between instances of a Person-class. In that case this collection should be declared as a class attribute or global list variable (not an instance attribute).
These samples of code are working:
with global list variable:
class Person:
def __init__(self, name):
self.name = name
def make_friends(self, list):
list.remove(self)
def __repr__(self):
return self.name
p1 = Person("Joe")
p2 = Person("Barack")
the_list = []
the_list.append(p1)
the_list.append(p2)
p1.make_friends(the_list)
print(the_list)
With class attribute:
class Person2:
class_list = []
def __init__(self, name):
self.name = name
Person2.class_list.append(self)
def make_friends(self):
Person2.class_list.remove(self)
def __repr__(self):
return self.name
p1 = Person2("Joe")
p2 = Person2("Barack")
print(Person2.class_list)
p1.make_friends()
print(Person2.class_list)
EDIT:
Variable 3 when a list of people is inside another class.
For accessing a list inside another class you could use attribute name or public method to get it if implemented:
class ClassWithList:
def __init__(self):
self.list_collection = []
def get_list(self):
return self.list_collection
class_with_list = ClassWithList()
class Person:
def __init__(self, name):
self.name = name
def make_friends(self, list):
list.remove(self)
def __repr__(self):
return self.name
p1 = Person("Joe")
p2 = Person("Barack")
# using implemented get-method of instance list attribute
class_with_list.get_list().append(p1)
class_with_list.get_list().append(p2)
print(class_with_list.get_list())
p1.make_friends(class_with_list.get_list())
print(class_with_list.get_list())
# instance list attribute of class`ClassWithList
print(class_with_list.list_collection)
p2.make_friends(class_with_list.list_collection)
print(class_with_list.list_collection)
Here is a MRE:
class Dog():
def __init__(self, action, name):
self.action = action
self.name = name
def name_dog(self):
my_name = input("give me a name: ")
return self.name(my_name)
def act_like_dog(self):
order = input("Tell me to do something: ")
msg = self.name + " is " + order
return self.action(msg)
def main(self):
self.name_dog()
self.act_like_dog()
Dog.main()
The main objective for calling the dog class in this case is for the user to input their own values (name, action) for the class. So the variables for the class I specified (Dog class) are obtained from a function call (self.name_dog and self.act_like_dog) in the main() function, which prompts the user to give his/her input. However when I tried calling the main() function without self, I got an error saying a positional argument is needed, while if I called the function self(), an error says that self is not defined. How do I fix this?
You seem to be confused about a few elements.
You need to create an actual instance of the class to be able to class its methods - Dog(...). Apart from that, there is a logical problem where you expect to create an instance with name and action but then take them as inputs from the user. I think what you should do is simply set the attributes with user input in the __init__. Something like:
class Dog():
def __init__(self):
self.name = self.name_dog()
self.action = self.act_like_dog()
def name_dog(self):
my_name = input("give me a name: ")
return my_name
def act_like_dog(self):
order = input("Tell me to do something: ")
msg = self.name + " is " + order
return msg
dog = Dog()
If you want to be able to change the name and action later, you can use your main idea:
class Dog():
def __init__(self):
main()
...
def main():
self.name = self.name_dog()
self.action = self.act_like_dog()
dog = Dog()
You might be looking for something like so:
class Dog:
def __init__(self, action=None, name=None):
if name:
self.name = name
else:
self.name = input("Dog's name: ")
if action:
self.action = action
else:
self.action = input("Dog's action: ")
d = Dog()
# ... or ...
d = Dog("Max", "Bark")
# ... or ...
d = Dog("Max")
d = Dog(name="Max")
# ... or ...
d = Dog(action="Bark")
So I have been making an RPG game and run into some troubles with inheritance and unpacking.
I have tried to make a simple version of the problem I am having:
class Animal():
def __init__(self,name):
self.name = name
def say_noise(self):
noise = "I am an animal"
by_who = "Animal"
return noise,by_who
class Dog(Animal):
def __init__(self,name):
self.name = name
def say_noise(self):
Animal.say_noise(self)
animal = Animal("man")
doggy = Dog("name")
What I want to do is return the same 2 things in the Animal class and the Dog class.
Now when I do say_noise() with my Animal it works fine, as it returns:
('I am an animal', 'Animal')
However, it does not return anything when I do it with the Dog class, and I tried unpacking them as well (which is what I aim to do in my real code), but when I do this.
a,b =doggy.say_noise()
it just returns this:
TypeError: cannot unpack non-iterable NoneType object
How can I return the same thing with the same class method, by only inheriting it from the parent class?
It's because you are not returning anything from Dog classes say_noise method.
Update your code like this,
class Animal():
def __init__(self, name):
self.name = name
def say_noise(self):
noise = "I am an animal"
by_who = "Animal"
return noise, by_who
class Dog(Animal):
def __init__(self, name):
self.name = name
def say_noise(self):
return Animal.say_noise(self)
animal = Animal("man")
doggy = Dog("name")
a, b = doggy.say_noise()
My simplified code is below: it creates an animal, and places it inside a zoo. I want to print the list of animals within the zoo. Going round in circles with this!
class Animal(object):
def __init__(self, name):
self.name = name
class Zoo(object):
def __init__(self):
self.animals = []
def __str__(self):
rep = ", ".join(self.animals)
return rep
def add(self, name):
self.animals.append(Animal(name))
def main():
while True:
zoo = Zoo()
animal = input("add an animal: ")
zoo.add(animal)
print(zoo)
main()
The added __repr__ Method to the Animal returns us the name.
The zoo = Zoo() has to be outside of the loop, this makes sure that we do not create a new zoo with every iteration.
Then we print the list (zoo.animals).
class Animal(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name
class Zoo(object):
def __init__(self):
self.animals = []
def __str__(self):
rep = ", ".join(self.animals)
return rep
def add(self, name):
self.animals.append(Animal(name))
def main():
zoo = Zoo()
while True:
animal = input("add an animal: ")
zoo.add(animal)
print(zoo.animals)
main()
You can simply refer to the name property of Animal in your Zoo.__str__(), e.g.:
def __str__(self):
return ', '.join(animal.name for animal in self.animals)
Now print(zoo) should work correctly.
However this doesn't provide a lot of encapsulation if say you wanted to change what it means to print an animal, e.g. height, size, etc. So perhaps a more encapsulated form would be:
class Animal(object):
...
def __str__(self):
return self.name
class Zoo(object):
...
def __str__(self):
return ", ".join(str(animal) for animal in self.animals)
Now when you print(zoo) the Animal class is responsible for its own string presentation.
Just as a note: you probably should create the Animal instance outside of Zoo, what happens if you decide to create a class hierarchy of Animals (e.g. Mammal) that has different behaviours, your Zoo class would only know about Animals.
class Animal(object):
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
class Zoo(object):
def __init__(self):
self.animals = []
def __str__(self):
return ", ".join(str(animal) for animal in self.animals)
def add(self, animal):
self.animals.append(animal)
def main():
zoo = Zoo()
while True:
animal = Animal(input("add an animal: "))
zoo.add(animal)
print(zoo)
main()
This would still behave properly if you create a Mammal class:
class Mammal(Animal):
...
zoo.add(Mammal(...))
print(zoo)
I am wondering if the following strategy is a proper/pythonic way to create a dynamic function within a method. The goal is to have a class that can calculate a value based on a complex model defined by FUN(), but I want to be able to change that model within a script without rewriting it, or creating a bunch of different types of classes (since this function is the only thing that I expect to change).
I also read in a response to this question that the way I have it structured may end up being slower? I intend to call setupFunction() 1 to 3 times a simulation (changing the model) and call FUN many thousands of times.
Pseudocode...
class MyClass:
def __init__(self, model = 'A'):
self.setupFunction(model)
# Other Class Stuff...
def setupFunction(self, _model):
if _model == 'A':
def myFunc(x):
# Do something with x, store in result
return result
else:
def myFunc(x):
# Do something different with x
return result
self.FUN = myFunc
# Other Class Methods... some of which may call upon self.FUN
Model1 = MyClass('A')
Model2 = MyClass('B')
print(Model1.FUN(10))
print(Model2.FUN(10))
I have done some minor tests and the above seems to not break upon first glance. I know I could also do something similar by doing the following instead, but then it will have to test for the model on each call to FUN() and I will have many different model cases in the end:
class MyClass():
def __init__(self, model = 'A'):
def FUN(self, x):
if self.model == 'A':
# result = Stuff
else:
# result = Other Stuff
return result
Still new to python, so thanks for any feedback!
Not sure if I understood your question...
What about something like this?
class MyClass():
model_func = {'A' : funca, 'B' : funcb}
def __init__(self, model):
self.func = self.model_func[model]
def funca():
pass
def funcb():
pass
a = MyClass('A')
a.func()
b = MyClass('B')
b.func()
Other option might be something like this (better separation of concerns):
class Base(object):
def __new__(cls, category, *arguments, **keywords):
for subclass in Base.__subclasses__():
if subclass.category == category:
return super(cls, subclass).__new__(subclass, *arguments, **keywords)
raise Exception, 'Category not supported!'
class ChildA(Base):
category = 'A'
def __init__(self, *arguments, **keywords):
print 'Init for Category A', arguments, keywords
def func(self):
print 'func for Category A'
class ChildB(Base):
category = 'B'
def func(self):
print 'func for Category B'
if __name__ == '__main__':
a = Base('A')
a.func()
print type(a)
b = Base('B')
b.func()
print type(b)
You can use __new__, to return different subclasses:
class MyClass():
def __new__(self, model):
cls = {'A': ClassA, 'B': ClassB}[model]
return object.__new__(cls)
class ClassA(MyClass):
def func():
print("This is ClassA.func")
class ClassB(MyClass):
def func():
print("This is ClassB.func")
a = MyClass('A')
a.func()
b = MyClass('B')
b.func()