Python class structure - python

I am trying to make my new code as user friendly as possible and what I would have in mind for the particular problem I am facing atm is this:
Suppose we have
import numpy as np
class TestClass:
def __init__(self, data):
self.data = data
#property
def method_a(self):
return np.median(self.data)
#property
def method_b(self):
return np.mean(self.data)
foo = TestClass([1, 2, 5, 7, 12, 6, 3, 37, 16])
print(foo.method_a)
print(foo.method_b)
Everything is fine so far. Method A gives me the median, method B the mean.
During processing I will switch depending on circumstances between both methods. So sometimes I will call method A, sometimes method B.
However, what I want is then to continue with a method C, that acts upon the result of either method A or B in such a way
final_result = foo.method_a.method_c
or
final_result = foo.method_b.method_c
I know it is possible to write method C as a function and do it like this:
final_result = method_c(foo.method_a)
final_result = method_c(foo.method_b)
but I think it would make the code easier to read if I could apply method C as stated above.
Is this possible somehow?
thanks

your statement is not quite clear, let's assume you want to add method C to the class. you can wrap your return value inside of the class again to achieve what you want:
import numpy as np
class TestClass:
def __init__(self, _data):
self._data = data
#property
def data(self): return self._data
#property
def method_a(self):
return TestClass(np.median(self.data))
#property
def method_b(self):
return TestClass(np.mean(self.data))
#property
def method_c(self):
return TestClass(np.whatever(self.data))
then you can chain however long you want:
final_result = foo.method_a.method_b.method_c.method_c.data
if the class is not what you plan to place, you put different one.

Following HuStmpHrrr's comment I changed my code like this
(Here I just assume that method C simply adds 1 to the results):
import numpy as np
class NewClass:
def __init__(self, data):
self.data = data
def method_c(self):
return self.data + 1
class TestClass:
def __init__(self, data):
self.data = data
#property
def method_a(self):
return NewClass(np.median(self.data))
#property
def method_b(self):
return NewClass(np.mean(self.data))
foo = TestClass([1, 2, 5, 7, 12, 6, 3, 37, 16])
result1 = foo.method_a
result2 = foo.method_b
print(result1.method_c())
print(result2.method_c())

I'm not sure why you want a property. Your code seems like it really just needs a simple method. Properties are for data that you would get and set that you want to manage.
class Test(Object):
def __init__(self, data):
super().__init__()
self.data = data
self.value = 0
# end Constructor
def method_a(self):
return np.median(self.data)
# end method_a
#property
def method_c(self):
return self.value
#method_c.setter
def method_c(self, value):
self.value = value
# self.method_a(value)
# end method_c
# end class Test
t = Test([1,2,3,4])
print(t.method_a())
t.method_c = 5 # This calls the method_c.setter and will make t.value = 5
print(t.method_c)
The property is typically used as a wrapper method to control the data.

Related

Can we pass the class object inside its own method?

I have a class A object method which uses another class B object's method, which the argument is class A object.
class A():
def calculate(self):
B = B.calculator(A)
class B():
def calculator(self, A):
...do something with A.attributes
It is possible to just pass attributes into the object, but I would see this possibility as the last priority. I am definitely a bit oversimplify my case, but I am wondering if there is a way to pass the entire class
Edit:
Sorry for the confusion. At the end I am trying to call class A object and A.calculate(), which will call class B obj and calculator function.
class A():
def __init__(self, value):
self.value = value
def calculate(self):
Bobj = B()
Bobj.calculator(A)
class B():
def calculator(self, A):
...do something with A.value
def main():
Aobj = A(value)
Aobj.calculate()
Your scenario does not currently indicate that you want to use any information from B when calculating A. There are a few ways of getting the functionality that you want.
Scenario: B stores no information and performs calculation. B should be a function
def B(value):
```do something with value```
return
class A():
def __init__(self, value):
self.value = value
def calculate(self):
return B(self.value)
def main():
Aobj = A(value)
Aobj.calculate()
Scenario: B stores some other information, but internal B information is not needed for the calculation. B should have a static method
class B():
#staticmethod
def calculate(value):
```do something with value```
return
class A():
def __init__(self, value):
self.value = value
def calculate(self):
return B.calculate(self.value)
def main():
Aobj = A(value)
Aobj.calculate()

How to have the same updated value of a Parent class be passed down to a inner class?

I need to access the value of an attribute defined at the parent class inside an inner class, here's the code:
class main(object):
def __init__(self):
self.session_id = None
self.devices = self.Devices(self.session_id)
class Devices(object):
def __init__(self, session_id):
self.session_id = session_id
And here's how I would like to use it:
>>> m = main()
>>> m.session_id = 1
>>> m.session_id
1
>>> m.devices.session_id
>>>
My expectation is that m.devices.session_id will always have the exact same value as m.session_id. I understand that at this point when I instantiate the inner class the session_id value is passed down as None because that's how it was initiated but I'm not sure how I can keep both values the same without doing something very ugly like:
m.devices.session_id = m.session_id
outside the class code.
How can I accomplish that inside the class itself ?
The other answer works, but I think this is a better design: lose the nested class, and add a getter on the device object to lookup a backref:
class Main(object):
def __init__(self):
self.session_id = None
self.devices = Devices(main_obj=self)
class Devices(object):
def __init__(self, main_obj):
self.main_obj = main_obj
...
#property
def session_id(self):
return self.main_obj.session_id
The difference here is that you're not storing the same data twice, so they can not get out of sync - there is only one "source of truth" for the session_id (on main object).
In the earlier answer, the data is actually stored in two different namespaces and will get out of sync as easily as m.devices.session_id = 123.
You can do it like this:
class main(object):
def __init__(self):
self._session_id = None
self.devices = self.Devices(self._session_id)
#property
def session_id(self):
return self._session_id
#session_id.setter
def session_id(self, value):
self._session_id = self.devices.session_id = value
class Devices(object):
def __init__(self, session_id):
self.session_id = session_id

Class method return list/array

I am trying to return a numpy array when calling a class method from a class initialized as a list.
import numpy as np
class test_object:
def __init__(self, val=1):
self.value = val
#staticmethod
def generate(number=5):
# create list of test_objects
obj = [test_object(val=i) for i in range(number)]
return obj
def powers(self, pow=2):
return self.value**pow
numbers = test_object.generate(number=10)
array = np.zeros(len(numbers))
for i in range(len(numbers)):
array[i] = numbers[i].powers()
print(array)
Is it possible to modify the class / class method 'powers' so, that it will
work like:
array = numbers.powers()
The array initializing and the for loop should be done inside the class method.
Here's a solution:
import numpy as np
class test_object():
def __init__(self, val=1):
self.value = val
class test_object_list(list):
def powers(self):
return np.asarray([i.powers() for i in self], dtype=float)
#classmethod
def generate(cls, number=5):
# create list of test_objects
obj = cls.test_object_list([cls(val=i) for i in range(number)])
return obj
def powers(self, pow=2):
return self.value**pow
numbers = test_object.generate(number=10)
array = numbers.powers()
print(array)
Note that I've replaced #staticmethod with #classmethod, as it is more appropriate.
I believe you need to extend the list functions.
A possible solutions could be the following:
# Extended subclass
class mylist(list):
def power(self, pow=2):
if self:
return[i.value**pow for i in self]
class test_object:
def __init__(self, val=1):
self.value = val
#staticmethod
def generate(number=5):
# create list of test_objects
obj = [test_object(val=i) for i in range(number)]
obj=mylist(obj)
return obj
def powers(self, pow=2):
return self.value**pow
numbers = test_object.generate(number=10)
print(numbers.power())
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
This answer is also based on another post:
Can I add custom methods/attributes to built-in Python types?

Python generic method scope

I've got a class that wraps functions with some metadata, in particular a parental relationship with other instances:
class Foo(object):
def __init__(self, func, parent):
self.func = func
self.parent = parent
self.parent_func = self.parent.func
In a few cases, I would like to use Foo to wrap a function that internally calls another Foo's function:
def f(x): return str(x).title()
def g(x): return self.parent_func(x)
a = Foo(f)
b = Foo(g, a)
print b.func("april is the cruellest month")
>>> April Is The Cruellest Month
Problem is that g isn't actually a method until b runs Foo.__init__, so it doesn't have a self.
I'm assuming there's something rather fundamental I'm missing about scoping, object methods, or functions' first-class citizenship status, and would greatly appreciate a point in the right direction.
EDIT: Looks like my above genericized example threw folks off, so I'm adding a more specific example below. The idea of this class is that each instance is an integer property (primality, perfection, its list of factors, etc), and contains a function that tests an integer for the property (returning a bool or an answer, as the case base be).
def f(n): # returns list of factors of n
def s(n): return len(self.parent_func(n))==2 # checks if n is semiprime
factors = Foo(f)
semiprime = Foo(s, factors)
It seems like your question boils down to "how can I dynamically add a method to an object", the the short answer is don't do it (1). Objects can have attributes which can be functions, and that's fine, but these functions do not become methods and don't behave like methods. For example if foo.attr is sum then foo.attr(x) is the same as sum(x) not sum(foo, x).
Your question has a certain functional "aroma" to it, if you wanted to drop the class/object stuff and go the fully functional route you could do something like this:
def identity(x):
return x
def f(n):
return [i for i in range(1, 10) if (n % i == 0)]
def s(factors):
return (len(factors) == 2)
def foo(func, helper=identity):
def innerfunc(n):
return func(helper(n))
return innerfunc
a = foo(f)
print a(6)
# [1, 2, 3, 6]
b = foo(s, a)
print b(5)
# True
If that doesn't appeal to you, I would suggest thinking of the func and parent attributes on your Foo class as data attached to your objects, not as methods, and work out the problem from there. The logic associated with your class should live inside proper methods. These methods can refer to the data as needed. Here's my very simple example:
class Foo(object):
def __init__(self, func, parent=None):
self.func = func
self.parent = parent
def run(self, n):
if self.parent is None:
return self.func(n)
else:
return self.func(self.parent.run(n))
a = Foo(f)
print a.run(6)
# [1, 2, 3, 6]
b = Foo(s, a)
print b.run(5)
# True
(1) Methods belong to a class not an object, so the question should really be how can I attach something to my object that behaves like a method.
As Matthew said, "parental relationship" would point to inheritance. But if you want/have to do it this way, you could use functools.partial:
from functools import partial
class Foo(object):
def __init__(self, func, parent=None):
self.func = partial(func, self)
self.parent = parent
self.parent_func = self.parent.func if parent is not None else None
def f(self, x):
return str(x).title()
def g(self, x):
return self.parent_func(x)
if __name__ == '__main__':
a = Foo(f)
b = Foo(g, a)
print b.func("april is the cruellest month")
When you call a object method, it is called with self as first parameter.
def f(self,x): return str(x).title()
def g(self,x): return self.parent_func(x)

Add a decorator to existing builtin class method in python

I've got a class which contains a number of lists where whenever something is added to one of the lists, I need to trigger a change to the instance's state. I've created a simple demonstration class below to try to demonstrate what I'm trying to do.
Suppose I have a class like this:
class MyClass:
added = False
def _decorator(self, f):
def func(item):
added = true
return f(item)
return func
def __init__(self):
self.list = [1, 2, 3]
self.list.append = self._decorator(self.list.append)
Since a list is built in, I cannot change it's .append method
cls = MyClass() #gives me an AttributeError since '.append' is readonly
Ideally, I could do the following:
cls = MyClass()
cls.list.append(4)
cls.added #would be true
How should I go about this? Would subclassing list allow me to change it's behavior in this way? If so, how would I pass in the class's state without changing the methods signature?
Thanks!
You cannot monkey-patch builtins, so subclassing is the only way (and actually better and cleaner IMHO). I'd go for something like this:
class CustomList(list):
def __init__(self, parent_instance, *args, **kwargs):
super(CustomList, self).__init__(*args, **kwargs)
self.parent_instance = parent_instance
def append(self, item):
self.parent_instance.added = True
super(CustomList, self).append(item)
class MyClass(object):
added = False
def __init__(self):
self.list = CustomList(self, [1,2,3])
c = MyClass()
print c.added # False
c.list.append(4)
print c.added # True
Would this suit your needs?
class MyClass(object):
added = False
def __init__(self):
self.list = [1,2,3]
def append(self, obj):
self.added = True
self.list.append(obj)
cls = MyClass()
cls.append(4)
cls.added #true
It might be helpful to know what exactly you're trying to achieve.

Categories