how to return object of matching subclass? - python

I'm trying to write a method that is supposed to return me an object of a subclass depending on some input data. Let me try to explain
class Pet():
#classmethod
def parse(cls,data):
#return Pet() if all else fails
pass
class BigPet(Pet):
size = "big"
#classmethod
def parse(cls,data):
#return BigPet() if all subclass parsers fails
pass
class SmallPet(Pet):
size = "small"
#classmethod
def parse(cls,data):
#return SmallPet() if all subclass parsers fails
pass
class Cat(SmallPet):
sound = "maw"
#classmethod
def parse(cls,data):
#return Cat() if all criteria met
pass
class Dog(BigPet):
sound = "woof"
#classmethod
def parse(cls,data):
#return Dog() if all criteria met
pass
Imagine that I would like to make a "parser", such as this:
Pet.parse(["big", "woof"])
> returns object of class Dog
Pet.parse(["small", "maw"])
> returns object of class Cat
Pet.parse(["small", "blup"])
> returns object of class SmallPet
I have no idea of how to write this in a proper way. Any suggestions? Of course this is a bullshit example. I'd like to apply this on different packets of a communication protocol of some kind.
If i am approaching this in a completely wrong way, please tell me :)

Why not pass the exact class name, look for that in the globals() and instantiate that?
def parse_pet(class_name, data):
# will raise a KeyError exception if class_name doesn't exist
cls = globals()[class_name]
return cls(data)
cat = parse_pet('Cat', 'meow')
big_pet = parse_pet('BigPet', 'woof')

Related

Calling a parents factory method during subclass instantation

The problem in code:
class SuperObject:
def __init__(self, property):
self.property = preprocess(property)
#classmethod
def create (cls, property):
super_object = cls.__new__(cls)
# dont preprocess!
super_object.property = property
return super_object
class Object (SuperObject):
def __init__(self, super_property, special_property):
super().__init__(super_property)
#problem: super_property gets preprocessed. How to use SuperObject.create here?
self.special_property = special_property
As mentioned in the code, I dont want to call the constructor of SuperObject, instead I want to use the classmethod to create the parent. The example of course is a simplification of my use case so please excuse if in itselft it makes only limited sense.

Not creating an object when conditions are not met in python?

Is it possible to not create an object if certain conditions are not met in the constructor of a class?
E.g.:
class ABC:
def __init__(self, a):
if a > 5:
self.a = a
else:
return None
a = ABC(3)
print(a)
This should print None (since it should not create an Object but return None in this case) but currently prints the Object...
you can use a classmethod as an alternate constructor and return what you want:
class ABC:
def __init__(self, a):
self.a = a
#classmethod
def with_validation(cls, a):
if a > 5:
return cls(a)
return None
a = ABC.with_validation(10)
a
<__main__.ABC at 0x10ceec288>
a = ABC.with_validation(4)
a
type(a)
NoneType
This code seems to show that an exception raised in an __init__() gives you the effect you want:
class Obj:
def __init__(self):
raise Exception("invalid condition")
class E:
def __call__(self):
raise Exception("raise")
def create(aType):
return aType()
def catchEx():
e = E()
funcs=[Obj, int, e]
for func in funcs:
try:
func()
print('No exception:', func)
except Exception as e:
print(e)
catchEx()
Output:
invalid condition
No exception: <class 'int'>
raise
I think this shows the principle. Note that returning None is not returning a new object because None is a singleton in Python, but of course it is still an object. Note also that __init__ will not be called as None is not an A class object.
class A():
def __new__(cls, condition):
if condition:
obj = super().__new__(cls)
return obj
a = A(True)
print(a)
a1 = A(False)
print(a1)
This outputs:
<__main__.A object at 0x7f64e65c62e8>
None
Edit:
I tried to directly address your question, by showing the new behaviour. But I think all the answers and comments here are good contributions.
So the proper answer is more about how you should do this kind of thing.
I recommend, more or less in this order depending on your taste and context:
"do the sane thing" by #Matt Messersmith. Test condition outside the class in client code and create the object only when appropriate.
"If the check is complicated and I want to make it easier for the user, it is better placed inside the class." by MrCarnivore. Maybe, maybe not. You can group validation code in functions inside a module that you import and call from the outside, still like in 1) mostly because validation rules can be repetitive or even apply to different kinds of objects. This also hides validation complexity from the client code.
raise an exception and use a try block in client code, by #Farhan.K. This is probably the more pythonic way if you test inside the class. You can still invoke an external data validation function inside the class for this.
define a classmethod in the class that acts as an alternate constructor by #salparadise. This is a good option.
go with a condition inside __new__ but do not pass it as an arg, or you have to use varargs to deal with that calls and __init__ calls. Then if you need varargs for other reasons, does not look a good option.
So I end up recommending several answers and options, except my own example. But I was only illustrating the main point of the question anyway.
With the help from #progmatico and a little try and error I managed to come to this solution:
class ABC:
def __new__(cls, *args, **kwargs):
if len(args) > 0:
arg = args[0]
else:
arg = kwargs['a']
if arg <= 5:
return None
return object.__new__(cls)
def __init__(self, a):
self.a = a
def __str__(self):
return str(self.a)
a = ABC(a=3)
print(a)
b = ABC(a=7)
print(b)

Declaring class attributes in __init__ vs with #property

If I'm creating a class that needs to store properties, when is it appropriate to use an #property decorator and when should I simply define them in __init__?
The reasons I can think of:
Say I have a class like
class Apple:
def __init__(self):
self.foodType = "fruit"
self.edible = True
self.color = "red"
This works fine. In this case, it's pretty clear to me that I shouldn't write the class as:
class Apple:
#property
def foodType(self):
return "fruit"
#property
def edible(self):
return True
#property
def color(self):
return "red"
But say I have a more complicated class, which has slower methods (say, fetching data over the internet).
I could implement this assigning attributes in __init__:
class Apple:
def __init__(self):
self.wikipedia_url = "https://en.wikipedia.org/wiki/Apple"
self.wikipedia_article_content = requests.get(self.wikipedia_url).text
or I could implement this with #property:
class Apple:
def __init__(self):
self.wikipedia_url = "https://en.wikipedia.org/wiki/Apple"
#property
def wikipedia_article_content(self):
return requests.get(self.wikipedia_url).text
In this case, the latter is about 50,000 times faster to instantiate. However, I could argue that if I were fetching wikipedia_article_content multiple times, the former is faster:
a = Apple()
a.wikipedia_article_content
a.wikipedia_article_content
a.wikipedia_article_content
In which case, the former is ~3 times faster because it has one third the number of requests.
My question
Is the only difference between assigning properties in these two ways the ones I've thought of? What else does #property allow me to do other than save time (in some cases)? For properties that take some time to assign, is there a "right way" to assign them?
Using a property allows for more complex behavior. Such as fetching the article content only when it has changed and only after a certain time period has passed.
Yes, I would suggest using property for those arguments. If you want to make it lazy or cached you can subclass property.
This is just an implementation of a lazy property. It does some operations inside the property and returns the result. This result is saved in the class with another name and each subsequent call on the property just returns the saved result.
class LazyProperty(property):
def __init__(self, *args, **kwargs):
# Let property set everything up
super(LazyProperty, self).__init__(*args, **kwargs)
# We need a name to save the cached result. If the property is called
# "test" save the result as "_test".
self._key = '_{0}'.format(self.fget.__name__)
def __get__(self, obj, owner=None):
# Called on the class not the instance
if obj is None:
return self
# Value is already fetched so just return the stored value
elif self._key in obj.__dict__:
return obj.__dict__[self._key]
# Value is not fetched, so fetch, save and return it
else:
val = self.fget(obj)
obj.__dict__[self._key] = val
return val
This allows you to calculate the value once and then always return it:
class Test:
def __init__(self):
pass
#LazyProperty
def test(self):
print('Doing some very slow stuff.')
return 100
This is how it would work (obviously you need to adapt it for your case):
>>> a = Test()
>>> a._test # The property hasn't been called so there is no result saved yet.
AttributeError: 'Test' object has no attribute '_test'
>>> a.test # First property access will evaluate the code you have in your property
Doing some very slow stuff.
100
>>> a.test # Accessing the property again will give you the saved result
100
>>> a._test # Or access the saved result directly
100

Python - Getting subclass from input. Do I have to write a separate function?

I'm working in Python 2.7.8. What follows is a slight variant of the problem I'm working on.
I have a large number of custom classes that I've written where the inheritance is like a tree. The behavior is well encapsulated by the following example:
import random
class Animal(object):
def __init__(self, name):
self.name = name
self.can_own_pets = False #most Animals cannot own pets
self.get_features()
def give_pet(self, pet):
if not self.can_own_pets:
print(self.name+' cannot own a pet!')
else:
self.pets.append(pet)
def is_hungry(self):
return random.choice([True, False])
def get_features(self):
"""
In some classes, get features will be a function
that uses self.name to extract features.
In my problem, the features are extracted
with regular expressions that are determined by
by the class.
"""
pass
class Human(Animal):
def __init__(self, name):
super(Human, self).__init__(name)
self.can_own_pets = True
self.pets = []
class Dog(Animal):
def __init__(self, name):
super(Dog, self).__init__(name)
def bark(self):
print 'WOOF'
def get_features(self):
if 'chihuahua' in self.name:
self.is_annoying = True
elif 'corgi' in self.name:
self.adorable = True
My program needs to take in a large number of animals and delegate them to the correct classes -- I need the correct attributes and methods. What I would like to do is modify the Animal constructor so that if the name argument is something like "Finn the Dog" or "Jake the Human", it (the constructor) returns an instance of the class "Dog" or "Human", complete with the appropriate methods and attributes. Now, I know that I could easily write a function that takes a string and class as arguments, constructs a dictionary where the keys are the names of the subclasses of the given class, looks up the element of the dictionary that is contained in the string, and returns an object of that class. My question is whether or not there is a way to code this into the Animal class itself, which seems more elegant to me (as well as easier to maintain).
Here's an implementation --
def _get_all_subclasses(cls):
for scls in cls.__subclasses__():
yield scls
for scls in _get_all_subclasses(scls):
yield scls
class Animal(object):
#staticmethod
def from_string(s):
for cls in _get_all_subclasses(Animal):
# Somehow pick the class based on the string... This is a really simple example...
if cls.__name__ in s:
return cls()
raise ValueError('Bummer. Animal has not been discovered.')
class Dog(Animal):
pass
class Cat(Animal):
pass
class Lion(Cat):
pass
print Animal.from_string('is a Dog')
print Animal.from_string('is a Cat')
print Animal.from_string('Lions!!!')
print Animal.from_string('Lockness Monster')
There are limitations here
All of the constructors need to be pretty much the same which means that Cat.__init__ needs to basically do the same thing that Human.__init__ does.
After you create the instance, your code needs to have logic to handle Cat, Human, Dog, etc. In some cases that's Ok (e.g. the code really only cares that it is working with an Animal), but frequently it isn't (after all, Cats can walk on fences, but Humans can't).
Generally, the principle that I like to live by is to try to make the inputs to my functions permissive (is it a list or a tuple? Who cares! Duck Typing FTW!) but to try to have really well defined outputs. I think that this makes interfaces easier to use in the long haul and the code that I wrote above would probably not pass a code review if I was the reviewer :-).
To build upon mgilson's answer
You can override the __new__ method so that you can instantiate the classes like normal without a static method.
class Animal(object):
#classmethod
def _get_all_subclasses(cls):
for scls in cls.__subclasses__():
yield scls
for scls in scls._get_all_subclasses():
yield scls
def __new__(cls, name):
cls_ = cls
for subcls in Animal._get_all_subclasses():
if subcls.__name__ in name:
cls_ = subcls
break
instance = object.__new__(cls_)
if not issubclass(cls_, cls):
instance.__init__(name)
return instance

python - setting property of class on module loading

I'm working on a code dealing with dict data on python.
While implementing such class, I have to define a lot of properties. It's not that hard, but recently I thought it would be much better if I could use something like helper function.
For example, let's assume that I have a class like the following.
class MyClass(object):
def __init__(self, data):
self.data = data
#property
def version(self):
return self.data["version"]
If I could write this class in something like the following.
class MyClass(object):
def __init__(self, data):
self.data = data
define_own_property("data", "version")
It looks trivial, but if I can do that, I think I can reuse a lot of validation/exception handling cases.
Any idea? :D
You can achieve something like that by just writing a function to return the accessor you want:
def define_own_property(attr, key):
def prop(self):
return getattr(self, attr)[key]
return property(prop)
class MyClass(object):
def __init__(self, data):
self.data = data
version = define_own_property("data", "version")
Note that you must do version = ... There is no way to make a simple function call define_own_property add a property to the class being defined, because that class doesn't yet exist so you can't reference it.
Another possibility is to give your class an attribute that is a list or dict or something containing the relevant parameters ("data", "version", etc.), then write a class decorator that reads these parameters and auto-creates the series of properties. This would remove the need to define the properties inside the class at all; you would just give a list of the things you wanted the properties to access, and use the decorator once on the class.
It seems like you could use a descriptor:
class Descr(object):
def __init__(self,attr,key):
self.attr = attr
self.key = key
def __get__(self,obj,type=None):
return getattr(obj,self.attr)[self.key]
def __set__(self,obj,value):
getattr(obj,self.attr)[self.key] = value
def __delete__(self,obj):
del getattr(obj,self.attr)[self.key]
class MyClass(object):
def __init__(self, data):
self.data = data
version = Descr("data","version")
foobar = Descr("data","foobar")
a = MyClass({})
a.version = 1
print a.version
a.foobar = 'string'
print a.data

Categories

Resources