Let's say I create a class "Animal" and make a child class "Cow", is it possible to make a method in "Animal" that I can call from the class "Cow" and that will generate a class of itself?
For example:
class Animal:
def __init__(self, identity, food, water):
self.identity = identity
self.food = food
self.water = water
def baby(self):
# make an object of the child class
class Cow(Animal):
pass
new_cow = Cow("id" + str(randint(100000, 999999)), 100, 10)
new_cow.baby() # make a new cow object
I don't even know how to start with the baby() method, but I hope you guys understand my question
def baby(self):
return type(self)()
type(self) gets you the class object of the current instance, e.g. Cow, and () makes a new instance of it, e.g. same as Cow(). Of course, Cow() requires three arguments, so either you have to provide them when you call baby, or otherwise pass them. E.g.:
def baby(self, *args):
return type(self)(*args)
...
new_cow.baby(42, 100, 10)
Or:
def baby(self):
return type(self)(self.identity + '1', self.food, self.water)
Related
I have a class with several functions.
From outside that class, I want to specify with a reference which function to call - but I'm not sure how.
For example, I have an Animal class with two functions sound and food. I want to write a Zoo class which receives one of the Animal's functions as input and applies that function to every animal instance it holds (the function all_animals_features).
class Animal:
def __init__(self, sound, food):
self.my_sound = sound
self.my_food = food
def sound(self):
# Do some complicated stuff....
return self.my_sound
def food(self):
return self.my_food
class Zoo():
def __init__(self, animals):
self.animals = animals
def all_animals_features(self, f):
return [animal.f() for animal in self.animals]
dog = Animal('Woof', 'Bone')
cat = Animal('Meow', 'Cream')
zoo = Zoo([cat, dog])
zoo.all_animals_features(Animal.sound)
But of course, 'Animal' object has no attribute 'f'.
Any idea how can this be implemented?
Clarification: if all that is needed, as demonstrated by this silly example, is just getting an attribute then it may be simpler to use getattr().
In your case you just need to adjust the way the method is called:
class Zoo():
def __init__(self, animals):
self.animals = animals
def all_animals_features(self, f):
return [f(animal) for animal in self.animals]
dog = Animal('Woof', 'Bone')
cat = Animal('Meow', 'Cream')
zoo = Zoo([cat, dog])
print(zoo.all_animals_features(Animal.sound))
Output:
['Meow', 'Woof']
Since you supply Animal.sound, as parameter f, the call in the list comprehension is: f(animal)
I would like to replace an object instance by another instance inside a method like this:
class A:
def method1(self):
self = func(self)
The object is retrieved from a database.
It is unlikely that replacing the 'self' variable will accomplish whatever you're trying to do, that couldn't just be accomplished by storing the result of func(self) in a different variable. 'self' is effectively a local variable only defined for the duration of the method call, used to pass in the instance of the class which is being operated upon. Replacing self will not actually replace references to the original instance of the class held by other objects, nor will it create a lasting reference to the new instance which was assigned to it.
As far as I understand, If you are trying to replace the current object with another object of same type (assuming func won't change the object type) from an member function. I think this will achieve that:
class A:
def method1(self):
newObj = func(self)
self.__dict__.update(newObj.__dict__)
It is not a direct answer to the question, but in the posts below there's a solution for what amirouche tried to do:
Python object conversion
Can I dynamically convert an instance of one class to another?
And here's working code sample (Python 3.2.5).
class Men:
def __init__(self, name):
self.name = name
def who_are_you(self):
print("I'm a men! My name is " + self.name)
def cast_to(self, sex, name):
self.__class__ = sex
self.name = name
def method_unique_to_men(self):
print('I made The Matrix')
class Women:
def __init__(self, name):
self.name = name
def who_are_you(self):
print("I'm a women! My name is " + self.name)
def cast_to(self, sex, name):
self.__class__ = sex
self.name = name
def method_unique_to_women(self):
print('I made Cloud Atlas')
men = Men('Larry')
men.who_are_you()
#>>> I'm a men! My name is Larry
men.method_unique_to_men()
#>>> I made The Matrix
men.cast_to(Women, 'Lana')
men.who_are_you()
#>>> I'm a women! My name is Lana
men.method_unique_to_women()
#>>> I made Cloud Atlas
Note the self.__class__ and not self.__class__.__name__. I.e. this technique not only replaces class name, but actually converts an instance of a class (at least both of them have same id()). Also, 1) I don't know whether it is "safe to replace a self object by another object of the same type in [an object own] method"; 2) it works with different types of objects, not only with ones that are of the same type; 3) it works not exactly like amirouche wanted: you can't init class like Class(args), only Class() (I'm not a pro and can't answer why it's like this).
Yes, all that will happen is that you won't be able to reference the current instance of your class A (unless you set another variable to self before you change it.) I wouldn't recommend it though, it makes for less readable code.
Note that you're only changing a variable, just like any other. Doing self = 123 is the same as doing abc = 123. self is only a reference to the current instance within the method. You can't change your instance by setting self.
What func(self) should do is to change the variables of your instance:
def func(obj):
obj.var_a = 123
obj.var_b = 'abc'
Then do this:
class A:
def method1(self):
func(self) # No need to assign self here
In many cases, a good way to achieve what you want is to call __init__ again. For example:
class MyList(list):
def trim(self,n):
self.__init__(self[:-n])
x = MyList([1,2,3,4])
x.trim(2)
assert type(x) == MyList
assert x == [1,2]
Note that this comes with a few assumptions such as the all that you want to change about the object being set in __init__. Also beware that this could cause problems with inheriting classes that redefine __init__ in an incompatible manner.
Yes, there is nothing wrong with this. Haters gonna hate. (Looking at you Pycharm with your in most cases imaginable, there's no point in such reassignment and it indicates an error).
A situation where you could do this is:
some_method(self, ...):
...
if(some_condition):
self = self.some_other_method()
...
return ...
Sure, you could start the method body by reassigning self to some other variable, but if you wouldn't normally do that with other parametres, why do it with self?
One can use the self assignment in a method, to change the class of instance to a derived class.
Of course one could assign it to a new object, but then the use of the new object ripples through the rest of code in the method. Reassiging it to self, leaves the rest of the method untouched.
class aclass:
def methodA(self):
...
if condition:
self = replace_by_derived(self)
# self is now referencing to an instance of a derived class
# with probably the same values for its data attributes
# all code here remains untouched
...
self.methodB() # calls the methodB of derivedclass is condition is True
...
def methodB(self):
# methodB of class aclass
...
class derivedclass(aclass):
def methodB(self):
#methodB of class derivedclass
...
But apart from such a special use case, I don't see any advantages to replace self.
You can make the instance a singleton element of the class
and mark the methods with #classmethod.
from enum import IntEnum
from collections import namedtuple
class kind(IntEnum):
circle = 1
square = 2
def attr(y): return [getattr(y, x) for x in 'k l b u r'.split()]
class Shape(namedtuple('Shape', 'k,l,b,u,r')):
self = None
#classmethod
def __repr__(cls):
return "<Shape({},{},{},{},{}) object at {}>".format(
*(attr(cls.self)+[id(cls.self)]))
#classmethod
def transform(cls, func):
cls.self = cls.self._replace(**func(cls.self))
Shape.self = Shape(k=1, l=2, b=3, u=4, r=5)
s = Shape.self
def nextkind(self):
return {'k': self.k+1}
print(repr(s)) # <Shape(1,2,3,4,5) object at 139766656561792>
s.transform(nextkind)
print(repr(s)) # <Shape(2,2,3,4,5) object at 139766656561888>
I'm trying to create a subclass in a particular case and I can not attach attributes or method to it. I think the new / init usage is not clear to me but I could not find ways to do that from the internet.
Here is a minimal working toy example showing what I am trying to do.
---- Edit of create_special_human() function
# I have this
class Human():
def __init__(self):
self.introduction = "Hello I'm human"
def create_special_human():
special_human = do_very_complicated_stuffs() #returns type Human
special_human.introduction = "Hello I'm special"
return special_human
# I want to create this class
class SuperHero(Human):
def __new__(self):
special_human = create_special_human()
return special_human
def __init__(self):
self.superpower = 'fly'
def show_off(self):
print(self.introduction)
print(f"I can {self.superpower}")
human = Human()
special_human = create_special_human()
super_hero = SuperHero()
super_hero.show_off() # fails with error "type object 'Human' has no attribute 'show_off'"
print(super_hero.superpower) # fails with error "type object 'Human' has no attribute 'superpower'"
I want to create the subclass Superhero, and I need to initialize it with what is returned by create_special_human(), because this function is very complex in the real case. Moreover, I can not modify the Human class and create_special_human().
I am aware that the returned type is Human, which is wrong, but I don't know why that happens.
(Edited)
I've made few changes to your code and it is executing successfuly.
You must call superclass __init__ inside of a subclass __init__:
As I said in the comment, simply return SuperHero from create_special_human.
I've removed __new__ method from SuperHero since it doesn't make any sense. Take a look at this article
class Human():
def __init__(self):
self.introduction = "Hello I'm human"
class SuperHero(Human):
# Removed __new__
def __init__(self):
super().__init__() # Init superclass
self.superpower = 'fly'
def show_off(self):
print(self.introduction)
print(f"I can {self.superpower}")
def create_special_human():
# Simply initialize and return SuperHero instance
special_human = SuperHero()
do_very_complicated_stuffs(special_human)
special_human.introduction = "Hello I'm special"
return special_human
human = Human()
special_human = create_special_human()
super_hero = SuperHero()
super_hero.show_off()
print(super_hero.superpower)
You can read more about super() in this question.
How do you access an instance in an object and pass it to another 'main' object? I'm working with a parser for a file that parses different tags, INDI(individual), BIRT(event), FAMS(spouse), FAMC(children)
Basically there are three classes: Person, Event, Family
class Person():
def __init__(self, ref):
self._id = ref
self._birth : None
def addBirth(self, event):
self._birth: event
class Event():
def __init__(self, ref):
self._id = ref
self._event = None
def addEvent(self, event):
self._event = event
#**event = ['12 Jul 1997', 'Seattle, WA'] (this is generated from a function outside a class)
I want to transfer self._event from the Event class into addBirth method to add it into my person class. I have little knowledge on how classes and class inhertiances work. Please help!
If I understand your question, you want to pass an (for example) Event object to an instance of Person?
Honestly, I don't understand the intent of your code, but you probably just need to pass self from one class instance to the other class instance.
self references the current instance.
class Person:
def __init__(self):
self._events = []
def add_event(self, event)
self._events.append(event)
class Event:
def add_to_person(self, person):
person.add_event(self)
The most proper way to handle situations like this is to use getter and setter methods; data encapsulation is important in OO programming. I don't always see this done in Python where I think it should, as compared to other languages. It simply means to add methods to your classes who sole purpose are to return args to a caller, or modify args from a caller. For example
Say you have class A and B, and class B (caller) wants to use a variable x from class A. Then class A should provide a getter interface to handle such situations. Setting you work the same:
class class_A():
def __init__(self, init_args):
x = 0
def someMethod():
doStuff()
def getX():
return x
def setX(val):
x = val
class class_B():
def init(self):
init_args = stuff
A = class_A(init_args)
x = class_A.getX()
def someOtherMethod():
doStuff()
So if class B wanted the x property of an instance object A of class class_A, B just needs to call the getter method.
As far as passing instances of objects themselves, say if you wanted A to pass an already-created instance object of itself to a method in class B, then indeed, you simply would pass self.
Here's the coding problem I am trying to solve... I have a base class, let's say Animal, and it has two subclasses, say Dog and Cat. My class Animal has a method, make_baby(), that both Dog and Cat will inherit. The trick I'm having trouble pulling off is that I want the return value to be a new instance of the subclass that called the function but with different attribute values, i.e. Dog.make_baby() should return a new Dog and Cat.make_baby() will return a new Cat.
I previously tried returning "type(self)()", but this is no good because type() return a type object, not a class.
Here is the full example code:
Class Animal():
def __init__(self, color):
self.color = color
def make_baby():
new_color = rand_color # a randomly chosen color
return #??? new class of the same type that called the method
Class Dog(Animal):
def pet():
print '*pant*'
Class Cat(Animal):
def pet():
print 'purrr'
So I'd like to avoid writing a make_baby() method for Dogs and Cats because the idea is that the method is exactly the same except for the returned class. I'd also like to avoid a bunch of if statements because I'd like to make and arbitrarily large number of subclasses to Animal.
You wrote:
this is no good because type() return a type object, not a class.
A type is a class, if you're using new-style classes. If you're using Python 3, you're set; all Python 3 classes are "new-style." If you're using Python 2.x, derive your class from object (or from something else that derives from object, like any built-in Python type).
But what you really want here is a class method, where you get a reference to the class passed in automatically.
class Animal(object):
def __init__(self, color):
self.color = color
#classmethod
def make_baby(cls):
return cls(rand_color) # randomly-chosen color
You can call it on the class (e.g. Animal.make_baby() or Dog.make_baby()) or on an instance; either way the method still receives the class as the first argument.
type() can be used to construct entirely new classes. What you want is:
class Animal():
def __init__(self, color):
self.color = color
def make_baby(self):
new_color = rand_color # a randomly chosen color
return self.__class__(new_color)
You approach will totally work! Just use new style classes.
Class Animal(object):
def __init__(self, color):
self.color = color
def make_baby(self):
new_color = rand_color # a randomly chosen color
return type(self)(new_color)
Class Dog(Animal):
def pet():
print '*pant*'
Class Cat(Animal):
def pet():
print 'purrr'
However, if make_baby(self) is not relying on details of self, what you want is a class-wide factory method, like in #Kindall's answer.