Python: Instance class mutation - python

I have two classes:
class Egg:
def __init__(self):
self._color = 'White'
class Larvae(Egg):
def __init__(self):
super().__init__()
self._color = 'Transparente'
To illustrate ...
So, in my code I have an Egg instance. When it's time I would like transform them into Larvae instances. I can create a Larvae instance and hand copy informations about a "previous" Egg instance. What does Python offer for something like that? For "mute" an instance in instance of subclass of its class?
Edit: As commented below, OOP in this question is not good way to do the wanted behavior. So, keep this in mind when reading answer

How about using a state-based approach?
class Ant:
def __init__(self, state='egg'):
self.state = state
#property
def color(self):
return {
'egg': 'Transparent',
'larvae': 'White,'
}[self.state]
def hatch(self):
if self.state == 'egg':
self.state = 'larvae'
else:
raise Exception('Cannot hatch if not an egg!')

This is possible, but not a great idea.
It is possible to change the type of an object after constructing it, but, well, it's just not robust. When you change the type of an object, its __init__() does not run. Any attributes left over from the old type are still there, and if they clash with attributes belonging to the new type, you probably have a nasty mess to deal with. If you're really sure this is the right approach, you can do it by assigning to the __class__ attribute (e.g. spam.__class__ = SomeClass). I strongly advise against this, however.
Instead, I would recommend factoring out the data you want to preserve into a "state" attribute, which you then transfer from one type to another. For example:
class Egg(object):
def __init__(self, state):
# Other egg-related stuff here...
self.state = state
def grow_up(self):
return Larva(self.state)
class Larva(object):
def __init__(self, state):
# Other larva-related stuff here...
self.state = state
def grow_up(self):
return Pupa(self.state)
class Pupa(object):
# and so on...
spam = Egg([1, 2, 3])
spam = spam.grow_up()
print(spam.state) # [1, 2, 3]

This answer has little to do with your request, but I find that it's worth to show that your OOP is a little bit counterintuitive. The whole point of OOP is that you create an intuitive inheritance hierarchy. There is little point in OO if your low level classes are supposed to be aware of higher level ones. In your use-case the Egg must be aware of the Larvae in order to transform, but that makes little sense. A parent class should not reference its child-classes.
class BaseInsect(object):
"""
Some stuff that all insects share at any stage
"""
def __init__(self, colour, *args, **kwargs):
self.colour = colour
...
class ImmatureAnt(BaseInsect):
...
class Egg(ImmatureAnt):
"""
Some egg-specific stuff
"""
def __init__(self, *args, **kwargs):
super(Egg, self).__init__("white")
...
class Larvae(ImmatureAnt):
def __init__(self, *args, **kwargs):
super(Larvae, self).__init__("transparent")
...
#staticmethod
def from_egg(egg, *args, **kwargs):
# make a larvae out of an egg
...
class BaseAdultAnt(BaseInsect):
"""
Some stuff all adult ants have
"""
...
class WorkerAnt(BaseAdultAnt):
...
class BaseReproducingAnt(BaseAdultAnt):
...
class Male(BaseReproducingAnt):
...
class Queen(BaseReproducingAnt):
...
Back to your question.
You've already been told that you can pass an Egg instance to the Larvae constructor. That will be a beautiful way. To make this answer a little bit less off-topic I'm giving an alternative solution. You might want to use several functions.
def egg_to_larvae(egg, *args, **kwargs):
"""
:param egg: an Egg instance
:type egg: Egg
"""
# do some stuff to get all the info needed to create a larvae...
return larvae
def larvae_to_pupae(larvae, *args, **kwargs):
...
return pupae
You get the idea

Just use a common base class instead of trying to inherit Larvae from an Egg:
class AntBase:
def __init__(self, color):
self._color = color
class Egg(AntBase):
def __init__(self, color='White'):
super().__init__(color)
class Larvae(AntBase):
def __init__(self, color='Transparente'):
super().__init__(color)
While Egg and Larvae are similar (both related to ants), Larvae is not an Egg, nor is an Egg a Larvae.
They are both "ant-things" though, thus we create a common AntBase class for all your ant things.
To convert an Egg into a Larvae, you can have a converter classmethod:
class AntBase:
def __init__(self, color):
self._color = color
#classmethod
def transform_from(cls, instance):
return cls(instance._color)
And now you can do:
egg0 = Egg()
larvae0 = Larvae.transform_from(egg0)
You can even have custom behaviour for each class:
class Larvae(AntBase):
#classmethod
def transform_from(cls, instance):
if isinstance(instance, AntMale):
raise ValueError("You can't have Larvae from AntMale")
return super().transform_from(instance)

Related

Python accessing parent class variable as instant variable in child class

I'm trying to access instance variables of a parent class as class variables in a child class.
The purpose is that the parent class will have a lot of child classes which all need to have the same structure, and a lot of different people will be working with and creating these child classes, without needing to know the inner workings of the parent class.
Here's my example:
class Human(ABC):
def __new__(cls, *args, **kwargs):
cls.human_name = args[0]
cls.source = f'database_{cls.__name__}'.lower()
return super().__new__(cls)
#property
#abstractmethod
def query(self):
pass
class Company:
class Employee(Human):
query = f'SELECT {human_name} FROM {source};'
# these two functions are just for testing and will not be in the final product
def print_something(self):
print(self.human_name)
def print_source(self):
print(self.source)
e = Company.Employee('John')
print(e.human_name)
print(e.query)
e.print_source()
I want to be able to create a child class of parent class Human (structured together in Company) where I only need to define the query variable which should automatically recognise the variables human_name and source.
How would I go about making this as simple as possible? Is this even possible?
Many thanks!
So, you need to actually implement the property.
class Human(ABC):
def __new__(cls, *args, **kwargs):
cls.human_name = args[0]
cls.source = f'database_{cls.__name__}'.lower()
return super().__new__(cls)
#property
#abstractmethod
def query(self):
pass
class Company:
class Employee(Human):
#property
def query(self):
return f'SELECT {self.human_name} FROM {self.source};'
# these two functions are just for testing and will not be in the final product
def print_something(self):
print(self.human_name)
def print_source(self):
print(self.source)
e = Company.Employee('John')
print(e.human_name)
print(e.query)
e.print_source()
Note, however, since __new__ creates class variables... this query will always be the same across instances:
employee1 = Company.Employee('John')
employee2 = Company.Employee('Jack')
print(employee1.query)
print(employee2.query)
will print:
SELECT Jack FROM database_employee;
SELECT Jack FROM database_employee;

UML diagrams in python?

I just got started learning about UML and I am currently trying to implement functionality for this diagram using python.
I have wrote the inheritance part, but I don't know yet how to deal with the association part of it. Could anyone give me some pointers on how to do this? Especially the circular reference.
My implementation so far:
class RailCar():
def __init__(self, name):
if len(name) >= 2:
self.__name = name
self.locomotive = None
else:
raise Exception("Name length of", type(self), " object must be at least 2 characters long.")
class PassengerCar(RailCar):
def __init__(self, capacity):
super().__init__(self, name)
self.__capacity = capacity
class RestaurantCar(RailCar):
def __init__(self, first):
super().__init__(self, name)
self.__first = first
class Locomotive():
def __init__(self, railCar):
self.railCar = railCar
rc01 = RailCar('01')
rc01.locomotive = "It works!!"
print(rc01.locomotive)
Supposedly the self reference is a doppelganger of the private next attribute. It should rather be written with the role name next to the association like so:
Anyhow, you would code it with self._next holding a reference to a RailCar instance. The getter/setter would then reference this private property.
class RailCar():
def __init__(self, name):
# as above
self._next = None
def setNext(self, next):
self._next = next
# you might check if the instance is RailCar.
#property
def getNext(self):
return self._next
P.S. The Locomotive-RailCar relation would be similar (with the same modeling issue). Just a self._first in Locomotive.

Can I call a subclass from a parent class to create the object

For example:
class parent(self):
def __init__(self, i):
self.i = i
def something(self, value):
a = child(value)
return a
class child(parent):
def something_that_is_not_init(self):
return self.i
The child class inherits init from the parent class. So my question is, in my parent class, can I create an instance of the child object, use and return it?
I would execute it as following:
a = parent(2)
b = a.something(3)
b.something_that_is_not_init()
3
Edited question a bit, updated code section since the question wasn't clear.
Yes, it's valid, but I don't recommend it. It's generally considered bad OOP programming. Also, you can create it as a static method so you never actually have to instantiate the parent class.
class parent():
def __init__(self, i):
self.i = i
#staticmethod
def foo(i):
c = child(i)
return c
class child(parent):
def bar(self):
print("Lucker number {}.".format(self.i)) # just to show the parent __init__ function is called
c = parent.foo(7)
c.bar() #=> Lucky number 7.
I just tried your example (including some missing self) and with python3 at least it works:
class Parent():
def __init__(self):
pass
def something(self):
a = child()
return a
class Child(parent):
def something_that_is_not_init(self):
print('yes, you got me')
and calling the something method works:
print(parent().something().__class__.__name__)
# Child
parent().something().something_that_is_not_init()
# yes, you got me
But maybe that's not very good design. Consider a factory or using __new__. But since you explicitly stated you wanted something like this: It works, even though I feel slightly spoiled writing it :-)

Python instance Decorator

I would like to decorate certain instance functions with a decorator from a "parent" instance, is there a way that I can use the instance to decorate the functions.
Here is the thought on what I need to do;
class Foo(object):
def __init__(self):
pass
def set_configuration(self, function):
def config(*args, **kwargs):
# NOTE: this function needs to use instance variables.
print 'foo ' + function()
return config()
class Bar(object):
def __init__(self, parent):
self.parent = parent
#self.parent.set_configuration
def set_config_2(self)
return 'bar'
foo = Foo()
foo.bar = Bar(foo)
foo.bar.set_config_2
EDIT:
Ok guys here is the actual issue, I have a device that i need to interact with. So a device may have several levels to it ie a device a
has multiple interfaces and an interface may have multiple vlans attached. So the idea is that if I want to change a vlan on an interface, instead of building a full command I would like to allow the parent class to handle the building of it's level of the command. So I would like to just call the "change vlan" function and it will send it's part of the command to the next level to be wrapped and sent up the chain till it hits the device level and the full command is sent to the device.
class Device(object):
def __init__(self):
self.interfaces = list()
self.ssh = ssh('blah')
def set_device(self, function):
self.ssh.command('setup commands')
self.ssh.command(wrapped command here)
self.ssh.command('exit commands')
class Interface(object):
def __init__(self, name, parent):
self.name
self.parent
self.vlan = Vlan('name')
def set_interface(self):
return self.name
class Vlan(object):
def __init__(self, name, parent):
self.name = name
self.parent = parent
def set_vlan(self):
return self.name
I hope this makes more sense. if not please let me know.
No, you cannot use decorators here, because at definition time of Bar, parent is not known.
Simply use set_configuration with a argument:
class Foo(object):
def __init__(self):
pass
def set_configuration(self, function):
def config(*args, **kwargs):
# NOTE: this function needs to use instance variables.
print 'foo ' + function()
return config
class Bar(object):
def __init__(self, parent):
self.parent = parent
def set_config_2(self, args)
def inner_function():
return 'bar'
return self.parent.set_configuration(inner_function)(args)
foo = Foo()
foo.bar = Bar(foo)
foo.bar.set_config_2(123)
Python is a dynamic language so many things are possible. I'm making no comment about whether this is a good thing to do or not - and I really can't understand the purpose of your logic.
To make this possible you will need dynamically create the set_config_2 in Bar.__init__ as parent is unknown at the class definition time:
from types import MethodType
class Foo(object):
def __init__(self):
pass
def set_configuration(self, f):
def config(inst, *args, **kwargs):
print('foo', f(inst, *args, **kwargs))
return config
class Bar(object):
def __init__(self, parent):
self.parent = parent
#self.parent.set_configuration
def set_config_2(inst):
return 'bar'
self.set_config_2 = MethodType(set_config_2, self)
foo = Foo()
foo.bar = Bar(foo)
foo.bar.set_config_2()
Output:
foo bar
This is desperately ugly and there must be a better way of doing what you are attempting. Perhaps you can ask a different question explaining what you are trying to achieve.
Your decorator does not have to use instance methods, since that's the wrapping function config who needs them. Therefore, the decorator does not have to be a method. For example:
def set_configuration(func):
#functools.wraps(func) # copy function's metadata
def wrapper(self, *args, **kwargs):
# do whatever you want to fetch the config data
return 'foo' + func(self, *args, **kwargs)
return wrapper
That said, there likely is a more straightforward and explicit way, depending on what exactly you want.
I'm pretty sure you can do this without making the decorator an instance. Here are a couple ideas.
Invert the hierarchy
It seems to me like the hierarchy you have is backwards. My understanding:
Device is only providing the ssh instance
The common method you want to call is something the VLAN defines
The setup and exit commands are constants
By making the hierarchy go the other way, you can define the "change VLAN" method to access stuff from the lower levels that it needs.
class Device(object):
def __init__(self):
self.ssh = ssh('blah')
class Interface(object):
def __init__(self, name, device):
self.name
self.device = device
class Vlan(object):
def __init__(self, name, change_command, interface):
self.name = name
# How you actually store this command is completely up to you.
# You might want to shove it in an abstract method
# and subclass Vlan, but the point is make it part of the
# Vlan somehow.
self.change_command = change_command
self.interface = interface
def change_vlan(self):
ssh = self.interface.device.ssh
ssh.command('setup commands')
ssh.command(self.change_command)
ssh.command('exit commands')
device1 = Device()
device2 = Device()
interface1 = Interface('i1', device1)
interface2 = Interface('i2', device1)
interface3 = Interface('i3', device2)
vlans = [
Vlan('v1', 'change 1', interface1)
Vlan('v2', 'change 2', interface1)
Vlan('v3', 'change 3', interface2)
Vlan('v4', 'change 4', interface3)
]
This might not show exactly what you want to do, but hopefully it demonstrates how you can set this up with the hierarchy going the other way.
Make The decorator accept a Device
Alternatively, if you still think decorating is a better option, you can make the decorate accept the instances you need.
def ssh_command(device, function):
def execute_ssh_command(*args, **kwargs):
device.ssh.command('setup commands')
device.ssh.command(wrapped command here)
device.ssh.command('exit commands')
# Note: no parentheses. You're returning the function itself
return execute_ssh_command
class Interface(object):
def __init__(self, name, parent):
self.name
self.parent
self.vlan = Vlan('name')
#ssh_command
def set_interface(self):
return self.name
Note you'll need to make a separate subclass per whatever thing uses the decorator.

How to pass an augment in super() correctly

My class takes an augment called resource:
> AClass(resource="123")
Class:
class AClass(Base):
def __init__(self, resource):
super(AClass, self).__init__(self)
Which will be set in the Base class it extends from.
class BaseHTTP(object):
def __init__(self, resource, data=None):
self.resource = resource
In Python 2.7 what should I be doing to make sure the base class gets these arguments, is this OK...
super(Get, self).__init__(self, resource)
You should not pass self to super(...).__init__, as super(...).__init__ returns the bound method:
class AClass(Base):
def __init__(self, resource):
super(AClass, self).__init__(resource)
Also, I'm not sure what Get is. Usually super's first argument should be the class from which it is called -- in this case, AClass.
Bonus trivia: super(...).__new__, in contrast, returns the staticmethod, since __new__ is a staticmethod. So for __new__, self must still be passed:
super(...).__new__(self, ...)
In addition to #unutbu's excellent answer, let me point out the canonical use of this idiom.
class Parent(object):
def __init__(self, name, species, gender):
self.name = name
self.color = color
self.species = species
self.gender = gender
self.children = []
def make_kid(self, partner, child_name):
if self.gender == "F":
return Child(mom=self, dad=partner, name=child_name,
species=self.species, gender=random.choice(["M", "F"]))
else:
return Child(mom=partner, dad=self, name=child_name,
species=self.species, gender=random.choice(["M", "F"]))
class Child(Parent):
def __init__(self, mom=None, dad=None, *args, **kwargs):
# a list of arguments we care about as a Child, followed by
# *args, **kwargs that other classes further up the MRO may need
self.mom = mom
self.dad = dad
# strip out the arguments we deal with here as a Child
super(Child, self).__init__(*args, **kwargs)
# then send the rest of them to the parent object
man = Parent("Adam","Human","M")
woman = Parent("Eve","Human","F")
child = man.make_kid(woman, "Junior")
I usually use:
class AClass(Base):
def __init__(self, resource):
Base.__init__(self, resource)
But #unutbu solution is better, I think.
Hope this helps.

Categories

Resources