I've got a few questions. Keep in mind i need to use deep copy as my classes will be expanding in complexity.
Is there a way to make it not reach the recursion limit when i do a deep copy?
When I preform a deep copy I want the new copy to be appended to the NODES variable just like it does in the init?
import copy
# Global
NODES = []
# Classes
class Node(object):
def __init__(self, name, age):
self.name = name
self.age = age
class Truck(Node):
def __init__(self, name="", age=0):
super(Truck, self).__init__(name=name, age=age)
NODES.append(self)
class Car(Node):
def __init__(self, name="", age=0):
super(Car, self).__init__(name=name, age=age)
NODES.append(self)
def __deepcopy__(self, memo):
print '__deepcopy__(%s)' % str(memo)
return Car(copy.deepcopy(self, memo))
Truck( name="Tonka Truck")
Truck( name="Monster Truck")
Truck( name="Pickup Truck")
car = Car( name="Oldsmobile Car")
car.age = 55
new_car = copy.deepcopy( car )
type_name = "Car"
cars = [x for x in NODES if type(x).__name__ == type_name]
print cars
print "NODES:"
for node in NODES:
print "\t", node.name, node.age
First, you really should use a defaultdict for Toys. It just meet this requirement If the superclass doesn't exists, it adds and appends the object. So let's go with
Toys = collections.defaultdict(list)
If you did not want to use copy.deepcopy, you could simply change Node.__init__ method to:
class Node(object):
def __init__(self, name, superclass):
self.name = name
self.superclass = superclass
Toys[superclass].append(self)
It works fine when you create a new Truck:
t = truck()
Toys['Trucks'][-1] is t
gives True
Unfortunately, deepcopy uses a special construction scheme and bypasses __init__ here.
But when __init__ can't do, just call __new__ to help...
__new__ is a lower level special method called as a class method to create the object before __init__ is called. And even deepcopy created objects are created with __new__. As it is a class method, you just need to declare the superclass names (BTW a superclass is another animal and you really should use a different name...) as a class attribute.
You code becomes:
import copy
import collections
# Globals
Toys = collections.defaultdict(list)
class Node(object):
def __new__(cls):
obj = super(Node, cls).__new__(cls)
superclass = cls.superclass
Toys[superclass].append(obj)
return obj
def __init__(self, name=""):
self.name = name
class Truck(Node):
superclass = "Trucks"
class Car(Node):
superclass = "Boats"
class Boat(Node):
superclass = "Nodes"
class Plane(Node):
superclass = "Planes"
t = Truck()
t.name = "Tonka Truck"
print Toys
t2 = copy.deepcopy( t )
print t, t2, Toys
With this output:
defaultdict(<type 'list'>, {'Trucks': [<__main__.Truck object at 0x0000000002D71A20>]})
<__main__.Truck object at 0x0000000002D71A20> <__main__.Truck object at 0x0000000002D71B70> defaultdict(<type 'list'>, {'Trucks': [<__main__.Truck object at 0x0000000002D71A20>, <__main__.Truck object at 0x0000000002D71B70>]})
That proves that:
Trucks list has automatically been added to Toys
t created as Truck() as been correctly added to Toys['Trucks']
t2 create with deepcopy as been correctly added to Toys['Trucks']
You now just have to change to superclass name for this code to be acceptable...
Related
I have this code in test.py:
class Parent(object):
def __init__(self):
self.myprop1 = "some"
self.myprop2 = "thing"
def duplicate(self):
copy = Parent()
copy.myprop1 = self.myprop1
copy.myprop2 = self.myprop2
return copy
And this other in test2.py:
from test import Parent
class Child(Parent):
def __str__(self):
return "{}, {}".format(self.myprop1, self.myprop2)
obj1 = Child()
obj2 = obj1.duplicate()
obj2.myprop1 = "another"
obj2.myprop2 = "stuff"
# Accessing properties
print("obj1, ", obj1.myprop1, ", ", obj1.myprop2)
print("obj2, ", obj2.myprop1, ", ", obj2.myprop2)
# Using Child method
print("obj1,", str(obj1))
print("obj2,", str(obj2))
Running test2.py, the output is:
obj1, some, thing
obj2, another, stuff
obj1, some, thing
obj2, <test.Parent object at 0x7fc1558e46d8>
I wonder if I can create an instance of Child inside Parent, but because there could be more child, I want to know the class of self and create an instance of that one class, copy the attributes and then return the object copy.
The goal for this code is to output this:
obj1, some, thing
obj2, another, stuff
obj1, some, thing
obj2, another, stuff
This means that obj2 is a Child object instead of a Parent object.
Hope this is clear, thanks!
EDIT: I don't want to use copy.copy() or copy.deepcopy(). If you want to get only a copy and implement a simpler solution, check Moberg comment to see another related question that uses those functions.
But, this question is intended to get another way of doing that and also know how to get the Class from an object and get another instance of that same Class. This particular case, is showing a relationship of parent-child between classes, that I added to show the whole context of my doubt.
Just don't hard-code the class, use type to retrieve the class of the instance, something like:
class Parent(object):
def __init__(self):
self.myprop1 = "some"
self.myprop2 = "thing"
def duplicate(self):
cls = type(self)
copy = cls()
copy.myprop1 = self.myprop1
copy.myprop2 = self.myprop2
return copy
To create the instance in duplicate you could use:
def duplicate(self):
copy = type(self)()
...
But a better solution would be to use copy.copy
Yes, use type(self) to get the type of the object. Also consider implementing the duplication with the dunder method __copy__ to support the builtin copy().
def __copy__(self):
return type(self)()
A class method might be better, as it gives you control over the type of object being created.
class Parent(object):
def __init__(self):
self.myprop1 = "some"
self.myprop2 = "thing"
#classmethod
def from_object(cls: 'T', obj: Parent) -> 'T':
copy = cls()
copy.myprop1 = obj.myprop1
copy.myprop2 = obj.myprop2
return copy
class Child(Parent):
def __str__(self):
return "{}, {}".format(self.myprop1, self.myprop2)
obj1 = Child()
obj2 = Child.from_object(obj1) # A new Child
obj3 = Parent.from_object(obj1) # A new Parent
I was expecting the object items inside other python objects are isolated.
However, the following code shows my expected result is wrong. It seems python uses a central item_list for all Group items. How can I fix this?
class Item:
name = ''
def __init__(self, name):
self.name = name
class Group:
item_list = []
def __init__(self, item):
self.item_list.append(item)
g2 = Group(Item('bb1'))
g2.item_list.append(Item('bb2'))
group_list = []
group_list.append(Group(Item('aaa')))
group_list.append(g2)
group_list.append(Group(Item('ccc')))
print('len = ', len(group_list[-1].item_list))
print('bb2 = ', group_list[1].item_list[1].name)
Result
len = 4
bb2 = bb1
Version
python3 --version
Python 3.5.2
Well, first of all we should make a difference between class attributes and instance attributes. A class attribute (like item_list) belongs to the class itself (in this case "Group"), so it will be accessible by calling Group.item_list. On the other hand, you can define a item_list for every instance of Group by defining self.item_list = [] inside the Group class constructor (__init__).
The Group.item_list array will be unique for the whole class, and thus will be suitable to store things that you want to share across instances (such as g2).
The self.item_list array (that will be different for each instance of Group) will hold values exclusively for the instance itself, so each Group will have its own item_list.
I think you are aiming for the second approach (instance variables) so you should move the definition of item_list = [] inside the class constructor.
The result should look like this:
class Item:
def __init__(self, name):
self.name = name
class Group:
def __init__(self, item):
self.item_list = []
self.item_list.append(item)
item1 = Item("itemName")
group1 = Group(item1)
# This should print an array containing the *item1* instance
print(group1.item_list)
print(group1.item_list[0] == item1)
Variables that are declared outside of the __init__ method (in this case item_list) are shared between all instances of a class (called class variables), which is why your expected result is wrong.
On the other hand, variables inside the __init__ only belong to the given instance of that class.
Your using class variables, which are similar C++ static variables inside classes (i.e. that variable is shared by ALL class instances). You need to put it inside the __init__ (constructor) to make it so each class creates its own version:
class Item:
def __init__(self, name):
self.name = name
class Group:
def __init__(self, item):
self.item_list = []
self.item_list.append(item)
# Though typically you would also have a function like this:
def add_item(self, item):
self.item_list.append(item)
g2 = Group(Item('bb1'))
g2.item_list.append(Item('bb2'))
group_list = []
group_list.append(Group(Item('aaa')))
group_list.append(g2)
group_list.append(Group(Item('ccc')))
print('len = ', len(group_list[-1].item_list))
print('bb2 = ', group_list[1].item_list[1].name)
Instance vs class attributes is covered in other answers. I want to add that you can avoid having shared instance variables by using an immutable type (e.g. tuple) instead of a mutable type (e.g. list) for class attributes. Like that they won't be shared among instances while still allowing you to define class attributes.
class Item:
name = ''
def __init__(self, name):
self.name = name
class Group:
item_list = tuple()
def __init__(self, item):
self.item_list += (item,)
I have the following class, which acts as a collection of people:
class Person:
PERSONS = dict() # name ==> instance
def __new__(cls, *args, **kwargs):
name = kwargs.get('name') or '' if not args else args[0]
print ('Name: %s' % name)
if name in cls.PERSONS:
print ('Returning found person!')
return cls.PERSONS[name]
else:
print ('Initializing new person')
return super(Person, cls).__new__(cls)
def __init__(self, name):
print ("Running init")
self.name = name
Person.PERSONS[name] = self
If a person is found, it returns that person, otherwise it creates a new one. And when I run it it works:
>>> p1 = Person('Julia')
Name: Julia
Initializing new person
Running init
>>> p2 = Person('Julia')
Name: Julia
Returning found person!
Running init # <== how to get this not to run?
>>> p1 is p2
True
However, if the person is found, I don't want the __init__ method to run. How would I "skip" the init method based on the return of the __new__ ?
One option is to add a conditional in the __init__, such as:
def __init__(self, name):
if name in Person.PERSONS: return # don't double-initialize
print ("Running init")
self.name = name
Person.PERSONS[name] = self
But I was hoping there might be a cleaner approach.
#MadPhysicist's idea of using a metaclass with a custom __call__ method is correct but the implementation included in the answer is quite off. Instead, the custom __call__ method should use the name of the person, rather than a new Person object, to check if a given name has an existing entry in the PERSONS dict:
class PersonMeta(type):
def __call__(cls, name):
print ('Name: %s' % name)
if name in cls.PERSONS:
print ('Returning found person!')
return cls.PERSONS[name]
print('Initializing new person')
obj = cls.__new__(cls, name)
cls.__init__(obj, name)
cls.PERSONS[name] = obj
return obj
class Person(metaclass=PersonMeta):
PERSONS = dict() # name ==> instance
def __init__(self, name):
print ("Running init")
self.name=name
p1=Person('Julia')
p2=Person('Julia')
print(p1 is p2)
This outputs:
Name: Julia
Initializing new person
Running init
Name: Julia
Returning found person!
True
Instead of trying to skip __init__, put your initialization in __new__. In general, most classes should only implement one of __new__ and __init__, or things get messy.
Also, trying to have a class act as a collection of anything is usually a bad idea. Instead of trying to make your class itself manage its instances, it tends to be a better idea to give that role to a dedicated collection object. This makes it easier to manage object lifetimes, have multiple containers, avoid weird __new__ problems, etc.
The problem I find in your approach is that the __new__ dunder method is triggered just before the __init__. Once said that, it's not that easy to change that behavior.
Instead of handling the new Person's creation inside __new__, create a class method (e.g. create_person) and update the PERSONS dict if needed.
class Person:
def __init__(self, name):
print("Running init\n")
self.name = name
class PersonFactory:
PERSONS = dict()
#classmethod
def create_person(cls, name):
print('Name: %s' % name)
if name in cls.PERSONS:
print ('Returning found person!')
return cls.PERSONS[name]
print('Initializing new person')
cls.PERSONS[name] = Person(name)
return cls.PERSONS[name]
if __name__ == '__main__':
PersonFactory.create_person('Julia')
PersonFactory.create_person('Julia')
I have a question which is more regarding OOP in general rather than python specific.
Is ist possible to store instances of ClassA in instance of ClassB without a specific method, i.e. by some kind of inheritance.
Example: let's say I have one Model class and one Variable class
class Model():
def __init__(self):
self.vars = []
def _update_vars(self,Variable):
self.vars.append(Variable)
class Variable(Model):
def __init__(self,**kwargs):
self.__dict__.update(kwargs)
Is it now possible to call _update_vars whenever an instance of variable is being created.
So if I do something like this:
mdl = Model()
varA = Variable(...)
varB = Variable(...)
that mdl.vars would now include varA and varB.
I know that I could easily do this by passing the variables as an argument to a "public" method of Model. So I am not looking for
mdl.update_vars(varA)
So my two questions are:
is this possible?
if yes: would this very non-standard OOP programming?
Thanks for your help!
That's not how class inheritance is supposed to work. You only want to inherit something if the child class is going to make use of a good amount of the attributes/methods within the parent class. If the child class has a markedly different structure it should be a class of its own.
In either case, as mentioned by #jasonharper, at some point you would need to give direction as to which Variable instance belongs in which Model instance, so you're likely to end up with something like these:
varA = Variable(mdl, ...)
# or this
mdl.varA = Variable(...)
With the first way, you would maintain the method on your Variable class:
class Foo:
def __init__(self):
self.vars = []
class Bar:
def __init__(self, foo_instance, **kwargs):
foo_instance.vars.append(self)
f = Foo()
b = Bar(f, hello='hey')
f.vars
# [<__main__.Bar object at 0x03F6B4B0>]
With the second way, you can append the Variable instances into a list each time it's added:
class Foo:
def __init__(self):
self.vars = []
def __setattr__(self, name, val):
self.__dict__.update({name: val})
if not name == 'vars': # to prevent a recursive loop
self.vars.append(val)
f = Foo()
f.vars
# []
f.a = 'bar'
f.vars
# ['bar']
Of course, an easier way would be to just look directly into the __dict__ each time you want vars:
class Bar:
#property
def vars(self):
# Or you can return .items() if you want both the name and the value
return list(self.__dict__.values())
b = Bar()
b.a = 'hello'
b.vars
# ['hello']
Both of these will work the same even if you assigned the attributes with your own class instances.
You can use super() for this and pass the instance to the parent
class Model():
vars = []
def __init__(self, other=None):
if other:
self.vars.append(other)
class Variable(Model):
def __init__(self, a):
self.a = a
super().__init__(self)
mdl = Model()
varA = Variable(3)
varB = Variable(4)
print(mdl.vars)
I would like to create an object that holds and creates different objects within itself.
I have an outer class and inner classes, like this:
class Outer:
def __init__(self, name):
self.name = name
def sayHello(self):
print "Hello " + self.name
class Inner1:
def __init__(self, name):
self.name = name
class Inner2(Inner1):
pass
class Inner3(Inner1):
pass
new = outer("new")
And then new needs to make on object of inner2 or inner3...
I tried it with new.inner2()
but I donĀ“t get the result I want.
Any tips?
Here is how you would do nested classes and nested instantiations. When you're embedding the classes, you're only embedding the types. You have to create the instances in self.__init__
(If you're trying to do global inner instances shared among all Outer instances please update your question.)
class Outer(object):
class Inner1(object):
pass
class Inner2(Inner1):
pass
class Inner3(Inner2):
pass
def __init__(self):
self.inner1 = Outer.Inner1()
self.inner2 = Outer.Inner2()
self.inner3 = Outer.Inner3()
outer = Outer()
print outer.inner1
print outer.inner2
print outer.inner3
Note that you don't have to actually use nested classes for this -- your classes can be defined outside of your class, and is sometimes preferred as simpler and more Pythonic:
class Inner1(object):
pass
class Inner2(Inner1):
pass
class Inner3(Inner2):
pass
class Outer(object):
def __init__(self):
self.inner1 = Inner1()
self.inner2 = Inner2()
self.inner3 = Inner3()
outer = Outer()
print outer.inner1
print outer.inner2
print outer.inner3
Sometimes you'll also see a pattern of...
class Inner1(object):
pass
class Outer(object):
Inner1 = Inner1
to make a "handy" reference to the class inside the class. This is often used with custom exceptions that the class might throw.
There are many different opinions on whether nesting the classes is preferred.
Honestly inner classes are not generally a good idea, especially if you're instantiating them outside of the "containing" class.
But to answer your question, basically the inner class is just declared in a different scope, so you need to reference the scope it is in.
# Changed to a capitol letter as that is more conventional
class Outer:
name = ""
def __init__(self, name):
self.name = name
def sayHello(self):
print ("Hello" + self.name)
class Inner1:
def __init__(self, name):
self.name = name
class Inner2(Inner1):
pass
class Inner3(Inner1):
pass
newOuter = Outer("newOuter")
newInner2 = Outer.Inner2("newInner2")