Embed a function from another file into a Python Class - python

I have some commonfunctions that I reuse as methods of different classes. I store these function in a commonfunctions.py file inside my module directory. I would like to embed the functions inside the class where I need it, and being able to modify the attributes of the classes. I have done some attempts but none of them is practical. I was wondering what is the best approach to do that.
commonfunctions.py
def mycommonfun(self,myarg):
self.target = myarg+3
class mydummyclass(object):
def mycommonfun(self,myarg):
self.target = myarg+3
mainfile.py
import commonfunctions
class A(anotherclass):
def __init__(self):
self.target = None
# not working
commonfunctions.mycommonfun
class B(anotherclass):
def __init__(self):
self.target = None
# I can't modify self.target
self.mycommonfun = commonfunctions.mycommonfun
class C(anotherclass):
# it works but it hides all the docstring
def mycommonfun(self,myarg):
commonfunctions.mycommonfun(self,myarg)
class D(anotherclas,commonfunctions.mydummyclass):
pass #it works not sure why I have problem with hinting of the arguments

What you need to do is inherit other class methods.
commonfunction.py
class ParentClass:
def __init__(self, target):
self.target = target
def some_function(self, n):
self.target = 3 + n
mainfile.py
from commonfunction import ParentClass
class ChildClass(ParentClass):
def __init__(self, target):
super().__init__(target)
child = ChildClass(5) # self.target = 5
child.some_function(5) # self.target = 3 + 5 = 8

Related

Python: Using property get/set to run on sub-class function

I have a setup that looks something like this simplified setup:
class WorkerBee():
def __init__(self):
self.x = 1
self.old_x = None
def update_x(self, val):
self.update_old_x()
self.x = val
def _update_old_x(self):
self.old_x = self.x
class MainClass():
def __init__(self):
self.bee = WorkerBee()
def updated_WorkerBee(self):
print('yay, it was updated')
I understand the use of #property to do a get - set setup for an attribute. But in this case, I'm trying to figure out how I can directly call the WorkerBee methods (there are more than 1 in my case....) that would also trigger MainClass.updated_WorkerBee()
In:
main = MainClass()
main.bee.update_x(2)
Out:
yay, it was updated
Any help is appreciated!
You could add a reference to the main object to the bee, like a parent:
class WorkerBee:
def __init__(self, main):
self.main = main
self.x = 1
(...)
and then use it in your update methods within WorkerBee, to update main too:
def update_x(self, val):
self.update_old_x()
self.x = val
self.main.updated_WorkerBee()
To do that, pass the main to the bee when you create it:
class MainClass:
def __init__(self):
self.bee = WorkerBee(self)

How do I save a variable in a class without the variable being lost through class inheritance

I want it so that when the active() function Is called in the StopSign class, it calls the stop() function in the Car class. However, in my actual code(code is kinda long and distractingly sloppy so I used this one so you can get the idea) when I call upon the active function StopSign object has no attribute 'movement_code'. How can I get around this?
class Car:
def __init__(self):
self.color = blue
more code
def go(self):
self.movement_code = movement_function
def stop:
self.movement_code.kill()
class StopSign(car):
def __init__(self):
some code
def active(self):
self.stop()
The problem you're having is that StopSign wasn't initializing its parent class and so didn't inherit the stop method. Besides that though I agree with Arya. It doesn't make sense to have the StopSign class inherit from the Car class.
class Car:
def __init__(self):
self.color = 'blue'
# more code
def go(self):
pass
# self.movement_code = movement_function
def stop(self):
pass
# self.movement_code.kill()
class StopSign(Car):
def __init__(self):
super().__init__()
pass
# some code
def active(self):
self.stop()
sign = StopSign()
sign.active()
Here is a simple example of how you could avoid to have StopSign subclass Car:
class Car:
def __init__(self):
self.color = 'blue'
def go(self):
pass
def stop(self):
pass
class StopSign:
def __init__(self):
self.cars_in_vicinity = []
def active(self):
for car in self.cars_in_vicinity:
car.stop()
car = Car()
sign = StopSign()
sign.cars_in_vicinity.append(car)
sign.active()
There are many other ways to approach this though.

Is there a "best practice" when passing a function from one class to another where neither one inherits from the other?

I have two classes which do NOT inherit from each other but one does need to call functions from the other. I made a simplified example using a Bathroom class and a Shower class.
The first method passes in the function as an one of the init arguments. The second overrides a variable after the first class is created.
Method 1
This seems like the proper way to do this but it can be tedious to have to pass in the function for every instance of Shower1 This would have to also be done for every other class that would require that function too; like Sink, Rug, Toliet, etc... It would be even more tedious if multiple functions needed to be passed into each one.
class Bathroom1:
def __init__(self):
self.items = []
def AddToItems(self, item):
self.items.append(item)
print('br1', str(self.items))
class Shower1:
def __init__(self, AddToItems):
self.AddToItems = AddToItems
self.AddToItems('Shower1')
bathroom1 = Bathroom1()
shower1= Shower1(bathroom1.AddToItems)
Method 2
This gets the same results as method 1 and I believe this would also be less tedious when there are multiple classes or multiple methods that need to be passed in. Rather than having to pass in the arguments for every new object created it would only have to be done once. But I'm not sure if this is considered "correct" or if it will lead to other problems.
class Bathroom2:
def __init__(self):
self.items = []
def AddToItems(self, item):
self.items.append(item)
print('br2', str(self.items))
class Shower2:
AddToItems = None
def __init__(self):
self.AddToItems('Shower2')
bathroom2 = Bathroom2()
Shower2.AddToItems = bathroom2.AddToItems
shower2 = Shower2()
I could use inheritance to make it easier to add other classes like Sink, Rug, etc...
Example With Inheritance:
class Bathroom3:
def __init__(self):
self.items = []
def AddToItems(self, item):
self.items.append(item)
print('br3', str(self.items))
class BathroomItem:
AddToItems = None
class Shower3(BathroomItem):
def __init__(self):
self.AddToItems('Shower3')
class Sink(BathroomItem):
def __init__(self):
self.AddToItems('Sink')
bathroom3 = Bathroom3()
BathroomItem.AddToItems = bathroom3.AddToItems
shower3 = Shower3()
sink = Sink()
Is there a recommended way to do this?
If the goal here is to add items to the bathroom, why not just pass the bathroom instance in whenever you're creating a new object?
class Bathroom:
def __init__(self):
self.items = []
def AddToItems(self, item):
self.items.append(item)
print('br', str(self.items))
class BathroomItem:
def __init__(self, bathroom):
bathroom.AddToItems(self)
br = Bathroom() # bathroom.items == []
item1 = BathroomItem(br) # bathroom.items == [item1]
item2 = BathroomItem(br) # bathroom.items == [item1, item2]

How to use super() to update a value in a specific instance of the parent?

I want to use the super function to write a string into a list of a specific instance of a parent object. Here's my code so far:
#!/usr/bin/env python
# test.py
class Master(object):
def __init__(self):
pass
class Sub1(Master):
list = []
def __init__(self):
pass
class Sub2(Sub1):
def __init__(self, name):
super(Sub2, self).list.append(name)
m = Master()
m.foo = Sub1()
m.foo.bar = Sub2('something')
m.bla = Sub1()
m.bla.blub = Sub2('else')
print(m.foo.list)
In this case the output is of course
['something', 'else']
However I want it to be just 'something'.
How can I achieve this?
I tried:
class Sub1(Master):
def __init__(self):
self.list = []
Which yields:
AttributeError: 'super' object has no attribute 'list'
Is there an easy solution?
As you have noted, if you define lst as a class attribute in Sub1, it is shared among all instances, which is not what you want. So you have to define lst as an instance attribute in Sub1 but then it has to be managed by an instance of Sub1 and not an instance of Sub2. Here is a modified code that offers what you expect:
class Master(object):
def __init__(self):
super().__init__()
class Sub1(Master):
def __init__(self):
super().__init__()
self.lst = []
def add(self, item):
self.lst.append(item.name)
class Sub2(Sub1):
def __init__(self, name):
super().__init__()
self.name = name
m = Master()
m.foo = Sub1()
m.foo.add(Sub2('something'))
m.bla = Sub1()
m.bla.add(Sub2('else'))
print(m.foo.lst)
print(m.bla.lst)
Here is the ouput:
['something'] <-- m.foo.lst
['else'] <-- m.bla.lst
Rem1: When using super() the whole class hierarchy has to be collaborative (i.e. use super() in the constructor).
Rem2: In Python3, super() is equivalent to super(classname, self)
Rem3: Don't use list as a variable name (it is a builtin type)
Rem4: My code stores only the name attribute in the list to mimic the example your gave, but I guess that in real life you would rather store instances of Sub2 in that list. To do so, simply remove the .name in the addfunction.
EDIT : Thinking a bit more about my answer, I came to another solution that may be closer to your initial attempt. Let me know which one works best for your actual problem...
class Master(object):
def __init__(self):
super().__init__()
class Sub1(Master):
def __init__(self):
super().__init__()
self.lst = []
class Sub2(Sub1):
def __init__(self, parent, name):
super().__init__()
parent.lst.append(name)
m = Master()
m.foo = Sub1()
m.foo.bar = Sub2(m.foo, 'something')
m.bla = Sub1()
m.bla.blub = Sub2(m.bla, 'else')
print(m.foo.lst)
print(m.bla.lst)
Your actual problem seems to be in the way you initialize list.
You need to assign it in __init__(), not within the class body, to avoid it being shared between all instances of the class (see Static class variables in Python).
class Sub1(Master):
def __init__(self):
self.list = []

Design Pattern to merge two implementations into one class

I can't figure out the correct way to model this problem.
Here I give you a minimalistic version of my code:
# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod
class AreaCalculator():
__metaclass__ = ABCMeta
def __init__(self):
pass
#abstractmethod
def getArea(self):
pass
def compute(self):
self.getArea()
class PerimeterCalculator():
__metaclass__ = ABCMeta
def __init__(self):
pass
#abstractmethod
def getPerimeter(self):
pass
def compute(self):
self.getPerimeter()
class TriangleAreaCalculator(AreaCalculator):
def __init__(self):
AreaCalculator.__init__(self)
def getArea(self):
return area
class TrianglePerimeterCalculator(PerimeterCalculator):
def __init__(self):
PerimeterCalculator.__init__(self)
def getPerimeter(self):
return perimeter
a = TriangleAreaCalculator()
b = TrianglePerimeterCalculator()
Is there an elegant way to merge "TrianglePerimeterCalculator" and "TriangleAreaCalculator" classes into one, but keeping "PerimeterCalculator" and "AreaCalculator" separated?
[edit] As Kyle suggested in the comments, I can create a new class (let's call it "Triangle") that inherits from "PerimeterCalculator" and "AreaCalculator" at the same time, but what I want is to be able to tell a new instance of "Triangle" to behave as "PerimeterCalculator" or "AreaCalculator", but not both at the same time.
I think the "design pattern" you should use is multiple inheritance. Below is a modified version of your code demonstrating how do it (plus a few other changes to make it actually runnable and all classes new-style).
from abc import ABCMeta, abstractmethod
class AreaCalculator(object):
__metaclass__ = ABCMeta
def __init__(self):
pass
#abstractmethod
def getArea(self):
pass
def compute(self):
self.getArea()
class PerimeterCalculator(object):
__metaclass__ = ABCMeta
def __init__(self):
pass
#abstractmethod
def getPerimeter(self):
pass
def compute(self):
self.getPerimeter()
class TriangleAreaCalculator(AreaCalculator):
def __init__(self):
super(TriangleAreaCalculator, self).__init__()
def getArea(self):
print('TriangleAreaCalculator.getArea() called on instance of {}'.format(
self.__class__.__name__))
# return area
return 13
class TrianglePerimeterCalculator(PerimeterCalculator):
def __init__(self):
super(TrianglePerimeterCalculator, self).__init__()
def getPerimeter(self):
print('TrianglePerimeterCalculator.getPerimeter() called on instance of {}'.format(
self.__class__.__name__))
# return perimeter
return 42
class MergedCalculator(TriangleAreaCalculator, TrianglePerimeterCalculator):
def __init__(self):
super(MergedCalculator, self).__init__()
merged = MergedCalculator()
print('merged.getArea() -> {}'.format(merged.getArea()))
print('merged.getPerimeter() -> {}'.format(merged.getPerimeter()))
Output:
TriangleAreaCalculator.getArea() called on instance of MergedCalculator
merged.getArea() -> 13
TrianglePerimeterCalculator.getPerimeter() called on instance of MergedCalculator
merged.getPerimeter() -> 42
Here's another answer, following the editing and clarification of your question. It allows creation of a single Triangle instance that can behave like either an AreaCalculator or PerimeterCalculator, as needed.
This programming pattern is called "delegation" and is used where the responsibility for implementing a particular operation is handed off to a different object—in this case an internally held instance of some other class. A common way to do this in Python is by overriding the class's default __getattr__() method.
Since you've never responded to the comment under my other answer about exactly what it is that controls which behavior is used, I added a set_behavior() method to allow it to be specified explicitly.
from abc import ABCMeta, abstractmethod
class AreaCalculator:
__metaclass__ = ABCMeta
def __init__(self):
pass
#abstractmethod
def getArea(self):
pass
def compute(self):
return self.getArea()
class PerimeterCalculator:
__metaclass__ = ABCMeta
def __init__(self):
pass
#abstractmethod
def getPerimeter(self):
pass
def compute(self):
return self.getPerimeter()
class TriangleAreaCalculator(AreaCalculator):
def __init__(self):
super(TriangleAreaCalculator, self).__init__()
def getArea(self):
print('TriangleAreaCalculator.getArea() called')
area = 13
return area
class TrianglePerimeterCalculator(PerimeterCalculator):
def __init__(self):
super(TrianglePerimeterCalculator, self).__init__()
def getPerimeter(self):
print('TrianglePerimeterCalculator.getPerimeter() called')
perimeter = 42
return perimeter
class Triangle:
def __init__(self):
delegate_classes = TriangleAreaCalculator, TrianglePerimeterCalculator
# Map delegate classes to instances of themselves.
self._delegates = {delegate_class: delegate_class()
for delegate_class in delegate_classes}
self.set_behavior(TriangleAreaCalculator) # Set default delegate.
def __getattr__(self, attrname):
# Called only for attributes not defined by this class (or its bases).
# Retrieve attribute from current behavior delegate class instance.
return getattr(self._behavior, attrname)
def set_behavior(self, delegate_class):
try:
self._behavior = self._delegates[delegate_class]
except KeyError:
raise TypeError("{} isn't a valid {} behavior delegate class"
.format(delegate_class, self.__class__.__name__))
if __name__ == '__main__':
triangle = Triangle()
# Uses instance's default behavior.
print('triangle.compute() -> {}'.format(triangle.compute()))
triangle.set_behavior(TrianglePerimeterCalculator) # Change behavior.
print('triangle.compute() -> {}'.format(triangle.compute()))
Output:
TriangleAreaCalculator.getArea() called
triangle.compute() -> 13
TrianglePerimeterCalculator.getPerimeter() called
triangle.compute() -> 42
I figured it out myself, with inspiration on the commentas/answers of Kyle and martineau.
I can create a merged class "Triangle" as follows:
class Triangle():
def __init__(self):
pass
def getTriangleArea(self):
print 'Triangle area'
def getTrianglePerimeter(self):
print 'Triangle perimeter'
And then modify TriangleAreaCalculator and TrianglePerimeterCalculator as follows:
class TriangleAreaCalculator(AreaCalculator, Triangle):
def __init__(self):
TriangleCalculator.__init__(self)
AreaCalculator.__init__(self)
def getArea(self):
super(TriangleAreaCalculator, self).getTriangleArea()
class TrianglePerimeterCalculator(PerimeterCalculator, Triangle):
def __init__(self):
TriangleCalculator.__init__(self)
PerimeterCalculator.__init__(self)
def getPerimeter(self):
super(TrianglePerimeterCalculator, self).getTrianglePerimeter()
This way, I can create a new Triangle-like instance that behaves as "PerimeterCalculator" or "AreaCalculator" (but not both at the same time):
a = TriangleAreaCalculator()
b = TrianglePerimeterCalculator()
a.compute() # correctly prints "Triangle area"
b.compute() # correctly prints "Triangle perimeter"

Categories