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')
Related
I've tried to make an OOP based program in python. I gave it an object to work with and tried to make it print the name, but its not working.
class human:
def __init__(self, name):
print("this is a human")
def name(self, name):
print("this is {}".format(bob.name))
bob = human("bob")
Anyone know what the problem could be?
Beyond the answers you already received (which solve your problem), I'd suggest not having a method that prints the name. Rather, you should have a __str___ dunder method that defines the object's behavior when an instance is printed.
class human:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
person = human("bob")
print(person)
'bob'
You can also define the object's behavior when the instance name is entered in the console, for instance just running the line
>>> person
You can do it with __repr__:
def __repr__(self):
return f'when entering the instance name in the console: {self.name}'
This will print:
when entering the instance name in the console: bob
This appears more pythonic to me than having a method that simply prints the name.
You're never storing the name on the instance, where would it get the name from? Your __init__ needs to do something along the lines of self.name = name
the name method and attribute are going to conflict, the latter will shadow (hide) the former, and it should look up whatever attribute its using on self
You never assigned the passed name to the object. Try:
class human:
def __init__(self, name):
print("this is a human")
self.name = name
def print_name(self):
print("this is {}".format(self.name))
bob = human("bob")
bob.print_name()
there are couple of things to update in the code:
bob is an instance which is not defined at human class
notice that init, name functions expect external param but you never use it in the function. (in self. = name)
in order to use it:
define a var in the class named 'name' and update you function to:
class human:
_name = ""
def __init__(self, name):
print("this is a human")
self._name = name
def name(self):
print("this is "+ self._name)
bob = human("bob")
bob.name()
bob = human("bob") only init function and you should call bob.name() in order to call the print-name function
I'm trying to initialize an objects field with a class that needs to know the type that is using it:
class Device(Model):
objects = AbstractManager(Device)
# the rest of the class here
This is how AbstractManager is defined:
class AbstractManager:
def __init__(self, cls: type):
self.cls = cls
def all(self):
result = []
for cls in self._get_subclasses():
result.extend(list(cls.objects.all()))
return result
def _get_subclasses(self):
return self.cls.__subclasses__()
So I can later call this and returns all() from all subclasses:
Device.objects.all()
The issue here is that I cannot use Device while initializing Device.objects, since Device is still not initialized.
As a work-around I'm initializing this outside of the class, but there's gotta be a better way:
class Device(Model):
objects = None
# the rest of the class here
Device.objects = AbstractManager(Device)
PD: I have a C#/C++ background, so maybe I'm thinking too much about this in a static-typing mindset, can't tell
You don't need to add any additional logic for this. Django allows you to access model class from manager using self.model attribute:
def _get_subclasses(self):
return self.model.__subclasses__()
You do not have to do that. Django will automatically call the contribute_to_class method, where it will pass the model, and for a manager, it will be stored in self.model. You can thus simply implement this as:
from django.db.models.manager import ManagerDescriptor
class AbstractManager(models.Manager):
def all(self):
result = []
for cls in self._get_subclasses():
result.extend(list(cls.objects.all()))
return result
def contribute_to_class(self, model, name):
self.name = self.name or name
self.model = model
setattr(model, name, AbstractManagerDescriptor(self))
model._meta.add_manager(self)
def _get_subclasses(self):
return self.model.__subclasses__()
class AbstractManagerDescriptor(ManagerDescriptor):
def __get__(self, instance, cls=None):
if instance is not None:
raise AttributeError("Manager isn't accessible via %s instances" % cls.__name__)
if cls._meta.swapped:
raise AttributeError(
"Manager isn't available; '%s.%s' has been swapped for '%s'" % (
cls._meta.app_label,
cls._meta.object_name,
cls._meta.swapped,
)
)
return cls._meta.managers_map[self.manager.name]
and add the manager as:
class Device(models.Model):
objects = AbstractManager()
That being said, I'm not sure that this is a good idea for two reasons:
you are returning a list, and normally .all() returns a QuerySet, you thus here "destroy" the laziness of the queryset, which can result in expensive querying; and
if one would use Device.objects.filter() for example, it would simply circumvent.
You might want to subclass the queryset, and then aim to implement that differently.
Why do I have to assign the result of types.MethodType() to an attribute before I use it via p.** in my example below. As I think, the method types.MethodType() has already bind the method onto the instance. Hope someone can help. Thanks a lot.
Here is my code:
import types
class Person(object):
def __init__(self, newName):
self.name = newName
def eat(self, food):
print("%s is eating %s" % (self.name, food))
def getName(self):
print("My name is %s" % self.name)
def main():
p = Person("Peter")
p.eat("Pork bean")
types.MethodType(getName, p) #p.getName = types.MethodType(getName, p)
p.getName()
if __name__ == "__main__":
main()
[]
[]
The following works to graft a method onto a class:
class A(object):
def __init__(self, value):
self.value = value
def my_method(self): # `self` is a plain name, not a reserved word or something.
return 'Value via a grafted method is %s' % self.value
A.grafted = my_method
assert A('here').grafted() == 'Value via a grafted method is here'
Yes, a plain assignment works.
Grafting a method onto an instance is harder but doable:
a = A('okay')
a.instance_patched = types.MethodType(my_method, a)
assert a.instance_patched() == 'Value via a grafted method is okay'
Here types.MethodType(my_method, a) creates a method bound to a.
Doing the following is elucidating:
print(my_method)
print(A.grafted)
print(a.instance_patched)
print(a.instance_patched.__func__) # in python 2.x, .im_func
Make sure that getName() is properly indented, on the same level as eat().
There is no need for types.MethodType() for you case:
class Person(object):
def __init__(self, newName):
self.name = newName
def eat(self, food):
print("%s is eating %s" % (self.name, food))
def getName(self):
print("My name is %s" % self.name)
def main():
p = Person("Peter")
p.eat("Pork bean")
p.getName()
if __name__ == "__main__":
main()
Output:
Peter is eating Pork bean
My name is Peter
Furthermore:
types.MethodType(getName, p)
does do anything useful, because you throw away the return value.
Because types.MethodType doesn't mutate either the function or the instance, it just returns a new callable object which holds both the function and the instance.
(You seem to have been expecting "bind" to mean "mutate one of the objects to hold a reference to the other", and so it sorta makes sense that you thought of it as adding the function onto the instance, but the best way to think of "bind" is that it produces a new function copy which has the instance bound as its first argument.)
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...
I know this one has been covered before, and perhaps isn't the most pythonic way of constructing a class, but I have a lot of different maya node classes with a lot #properties for retrieving/setting node data, and I want to see if procedurally building the attributes cuts down on overhead/mantinence.
I need to re-implement __setattr__ so that the standard behavior is maintained, but for certain special attributes, the value is get/set to an outside object.
I have seen examples of re-implementing __setattr__ on stack overflow, but I seem to be missing something.
I don't think i am maintaining the default functionality of setAttr
Here is an example:
externalData = {'translateX':1.0,'translateY':1.0,'translateZ':1.0}
attrKeys = ['translateX','translateY','translateZ']
class Transform(object):
def __getattribute__(self, name):
print 'Getting --->', name
if name in attrKeys:
return externalData[name]
else:
raise AttributeError("No attribute named [%s]" %name)
def __setattr__(self, name, value):
print 'Setting --->', name
super(Transform, self).__setattr__(name, value)
if name in attrKeys:
externalData[name] = value
myInstance = Transform()
myInstance.translateX
# Result: 1.0 #
myInstance.translateX = 9999
myInstance.translateX
# Result: 9999 #
myInstance.name = 'myName'
myInstance.name
# AttributeError: No attribute named [name] #
!
This worked for me:
class Transform(object):
def __getattribute__(self, name):
if name in attrKeys:
return externalData[name]
return super(Transform, self).__getattribute__(name)
def __setattr__(self, name, value):
if name in attrKeys:
externalData[name] = value
else:
super(Transform, self).__setattr__(name, value)
However, I'm not sure this is a good route to go.
If the external operations are time consuming (say, you're using this to disguise access to a database or a config file) you may give users of the code the wrong impression about the cost. In a case like that you should use a method so users understand that they are initiating an action, not just looking at data.
OTOH if the access is quick, be careful that the encapsulation of your classes isn't broken. If you're doing this to get at maya scene data (pymel-style, or as in this example) it's not a big deal since the time costs and stability of the data are more or less guaranteed. However you'd want to avoid the scenario in the example code you posted: it would be very easy to assume that having set 'translateX' to a given value it would stay put, where in fact there are lots of ways that the contents of the outside variables could get messed with, preventing you from being able to know your invariants while using the class. If the class is intended for throwaway use (say, its syntax sugar for a lot of fast repetitive processing inside as loop where no other operations are running) you could get away with it - but if not, internalize the data to your instances.
One last issue: If you have 'a lot of classes' you will also have to do a lot of boilerplate to make this work. If you are trying to wrap Maya scene data, read up on descriptors (here's a great 5-minute video). You can wrap typical transform properties, for example, like this:
import maya.cmds as cmds
class MayaProperty(object):
'''
in a real implmentation you'd want to support different value types,
etc by storing flags appropriate to different commands....
'''
def __init__(self, cmd, flag):
self.Command = cmd
self.Flag = flag
def __get__(self, obj, objtype):
return self.Command(obj, **{'q':True, self.Flag:True} )
def __set__(self, obj, value):
self.Command(obj, **{ self.Flag:value})
class XformWrapper(object):
def __init__(self, obj):
self.Object = obj
def __repr__(self):
return self.Object # so that the command will work on the string name of the object
translation = MayaProperty(cmds.xform, 'translation')
rotation = MayaProperty(cmds.xform, 'rotation')
scale = MayaProperty(cmds.xform, 'scale')
In real code you'd need error handling and cleaner configuration but you see the idea.
The example linked above talks about using metaclasses to populate classes when you have lots of property descriptors to configure, that is a good route to go if you don't want to worry about all the boilerplate (though it does have a minor startup time penalty - I think that's one of the reasons for the notorious Pymel startup crawl...)
I have decided to go with #theodox s and use descriptors
This seems to work nicely:
class Transform(object):
def __init__(self, name):
self.name = name
for key in ['translateX','translateY','translateZ']:
buildNodeAttr(self.__class__, '%s.%s' % (self.name, key))
def buildNodeAttr(cls, plug):
setattr(cls, plug.split('.')[-1], AttrDescriptor(plug))
class AttrDescriptor(object):
def __init__(self, plug):
self.plug = plug
def __get__(self, obj, objtype):
return mc.getAttr(self.plug)
def __set__(self, obj, val):
mc.setAttr(self.plug, val)
myTransform = Transform(mc.createNode('transform', name = 'transformA'))
myTransform.translateX = 999
As a side note...
It turns out my original code would have worked just by switching getattribute with getattr
no super needed
Why not also do the same thing in __getattribute__?
def __getattribute__(self, name):
print 'Getting --->', name
if name in attrKeys:
return externalData[name]
else:
# raise AttributeError("No attribute named [%s]" %name)
return super(Transform, self).__getattribute__(name)
Test code
myInstance = Transform()
myInstance.translateX
print(externalData['translateX'])
myInstance.translateX = 9999
myInstance.translateX
print(externalData['translateX'])
myInstance.name = 'myName'
print myInstance.name
print myInstance.__dict__['name']
Output:
Getting ---> translateX
1.0
Setting ---> translateX
Getting ---> translateX
9999
Setting ---> name
Getting ---> name
myName
Getting ---> __dict__
myName
Here in your snippet:
class Transform(object):
def __getattribute__(self, name):
print 'Getting --->', name
if name in attrKeys:
return externalData[name]
else:
raise AttributeError("No attribute named [%s]" %name)
def __setattr__(self, name, value):
print 'Setting --->', name
super(Transform, self).__setattr__(name, value)
if name in attrKeys:
externalData[name] = value
See, in your __setattr__() when you called for myInstance.name = 'myName', name is not in attrKeys, so it doesn't insert into externalData dictionary but it add into self.__dict__['name'] = value
So, when you try to lookup for that particular name, you don't ve into your externalData dictionary so your __getattribute__ is raise with an exception.
You can fix that by changing the __getattribute__ instead of raising an exception change as below :
def __getattribute__(self, name):
print 'Getting --->', name
if name in attrKeys:
return externalData[name]
else:
return object.__getattribute__(self, name)