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?
Related
So I want to call method on returned value from another method of same class.
class A():
def __init__(self,data):
self.data = data
def values(self,key):
return list(list(x.values())[0] for x in self.data['data'] if key in x.keys())
def length(self):
return len(self)
data ={"data":[{"country":"india"},{"state":"punjab"},{"country":"usa"}]}
obj = A(data)
res = obj.values('country') # returns ['india', 'usa']
res1 = obj.values('country').length() # returns AttributeError: 'list' object has no attribute 'length'
print(res)
print(res1)
i want both res and res1 to work.
I have also tried using #property decorator
class B():
def __init__(self,data):
self.data = data
def __call__(self, key):
self.key = key
return self
#property
def values(self):
self.valu = list(list(x.values())[0] for x in self.data['data'] if self.key in x.keys())
return self
def length(self):
return len(self.valu)
data ={"data":[{"country":"india"},{"state":"punjab"},{"country":"usa"}]}
obj = B(data)
res = obj('country').values.length() # returns 2
res1 = obj('country').values # returns <__main__.A object at 0x103a9fbe0>
print(res)
print(res1)
This way res works but res1 does not.
Thanks
.length() is not a python list function. Use len(listobject) instead.
Eg:
len(obj('country').values)
If you want to print the values. In your second example:
#Use
res1 = obj('country').data
#instead of
res1 = obj('country').values
Use below code to get list of countries
res1 = [_dict.get('country') for _dict in obj('country').data.get('data') if 'country' in _dict.keys()]
print(res1)
#['india', 'usa']
Final Updated working code:
class B():
def __init__(self,data):
self.data = data
def __call__(self, key):
self.key = key
return self
#property
def values(self):
self.valu = list(list(x.values())[0] for x in self.data['data'] if self.key in x.keys())
return self
def length(self):
return len(self.valu)
data ={"data":[{"country":"india"},{"state":"punjab"},{"country":"usa"}]}
obj = B(data)
res = obj('country').values.length() # returns 2
res1 = [_dict.get('country') for _dict in obj('country').data.get('data') if 'country' in _dict.keys()] # returns ['usa', 'india']
print(res)
print(res1)
I think the confusion here is on what object is the .length method invoked. In the first case, it is invoked on a list which does not have a .length method. In the second case, it is invoked on the B object which indeed has a .length method. The most straightforward and preferred solution is what #Subhrajyoti Das suggested.
If you still want your code (as described in the question) to work (or just want to know if this could actually be done), you could define a custom list object which would look like as follows:
class CustomList(list):
def length(self):
return len(self)
Now instead of making a list, you would make a CustomList.
Edit: Adding the complete example, as requested in the comment.
class CustomList(list):
def length(self):
return len(self)
class A():
def __init__(self,data):
self.data = data
def values(self,key):
return CustomList(CustomList(x.values())[0] for x in self.data['data'] if key in x.keys())
def length(self):
return len(self)
data ={"data":[{"country":"india"},{"state":"punjab"},{"country":"usa"}]}
obj = A(data)
res = obj.values('country') # returns ['india', 'usa']
res1 = obj.values('country').length() # 2
object has no attribute 'length'
print(res)
print(res1)
As a C++ programmer the following code seems very natural to me but it doesn't run:
from typing import TypeVar, Generic, List, NewType
TPopMember = TypeVar('TPopMember')
Population = NewType('Population', List[TPopMember])
class EvolutionaryAlgorithm(Generic[TPopMember]):
def __init__(self, populationSize: int) -> None:
# The following raises TypeError: 'TypeVar' object is not callable
self.__population = Population([TPopMember() for _ in range(populationSize)])
Apparently Python isn't able to instantiate classes (the TPopMember) that are actually TypeVars. I simply want to create a list (Population) with a couple of default initialized (how do you say that in Python?) TPopMembers. How should I go about this?
I'm using Python 3.7.2.
You didn't realize that type hint is a hint. In other words, don't think it is a type at all. You can't instantiate them.
As I understand from your comment, your intention is doing what C++ template allows you to do. So here is my way to achieve that:
from typing import TypeVar, Generic, List, NewType, Type
import random
class PopMember:
def __init__(self):
self.x = random.randint(0, 100)
def __repr__(self):
return "Pop({})".format(self.x)
TPopMember = TypeVar("TPopMember")
Population = NewType('Population', List[TPopMember])
class EvolutionaryAlgorithm(Generic[TPopMember]):
def __init__(self, member_class: Type[TPopMember], populationSize: int) -> None:
self.__population = Population([member_class() for _ in range(populationSize)])
def __repr__(self):
return "EA({})".format(self.__population)
x = EvolutionaryAlgorithm(PopMember, 5)
print(x)
output:
EA([Pop(49), Pop(94), Pop(24), Pop(73), Pop(66)])
What you have to understand is that, if you derived a class from Generic[T], you need to use T some how when you create your class. In my example I create a dummy object and resolve its class and initiate it. Normally I would not write in this way, I can just throw in a class as parameber pass in a class to the constructor to request to generate items of this particular type because class itself, distinct from an instance of it, is also a Python object. (thanks chepner for the suggestion)
You can do the following:
from typing import TypeVar, Generic, List, NewType
import random
class PopMember:
def __init__(self):
self.x = random.randint(0, 100)
def __repr__(self):
return "Pop({})".format(self.x)
TPopMember = TypeVar('TPopMember')
Population = NewType('Population', List[TPopMember])
class EvolutionaryAlgorithm(Generic[TPopMember]):
def __init__(self, populationSize: int) -> None:
obj = self.__orig_class__.__args__[0]
self.__population = Population([obj() for _ in range(populationSize)])
#property
def population(self):
return self.__population
evolution = EvolutionaryAlgorithm[PopMember](100)
print(evolution.population)
The type used to define the Generic class can be found, within the instance in here: self.__orig_class__.__args__[0].
For classmethods just use this -> cls.__args__[0]
There is another possibility that avoid problems when serializing classes (ie using pickle).
Instead of using Generic, you can do the following:
from typing import Callable, Any
import random
from enum import Enum
from functools import wraps
class PopMember:
def __init__(self):
self.x = random.randint(0, 100)
def __repr__(self):
return "Pop({})".format(self.x)
class PapMember:
def __init__(self):
self.x = random.randint(0, 200)
def __repr__(self):
return "Pop({})".format(self.x)
def check_type(func: Callable) -> Callable:
"""Decorator to check that the child class has defined the POINT_TYPE member attribute."""
#wraps(func)
def wrapper(obj, *args, **kwargs) -> Any:
if not hasattr(obj, 'T'):
raise NotImplementedError(
"You can not instantiate an abstract class.")
return func(obj, *args, **kwargs)
return wrapper
class EvolutionaryAlgorithm:
#check_type
def __init__(self, populationSize: int) -> None:
self.__population = [self.T() for _ in range(populationSize)]
#classmethod
#check_type
def create(cls, populationSize: int):
"""Example of classmethod."""
# You can use T as cls.T
return cls(populationSize=populationSize)
#property
def population(self):
return self.__population
class EvolutionaryAlgorithmPopMember(EvolutionaryAlgorithm):
T = PopMember
class EvolutionaryAlgorithmPapMember(EvolutionaryAlgorithm):
T = PapMember
class EvolutionaryAlgorithmFactory(Enum):
POP_MEMBER = EvolutionaryAlgorithmPopMember
PAP_MEMBER = EvolutionaryAlgorithmPapMember
def __call__(self, *args, **kwargs) -> Any:
return self.value(*args, **kwargs)
def __str__(self) -> str:
return self.name
evolution = EvolutionaryAlgorithmFactory.POP_MEMBER(100)
print(evolution.population)
This will avoid a lot of problems, rather than hacking python internals.
The main advantage in here is that you can reuse the classmethod functions.
A much easy solution could be to use functools.partial in an enum factory:
import random
from typing import Any
from enum import Enum
from functools import partial
class PopMember:
def __init__(self):
self.x = random.randint(0, 100)
def __repr__(self):
return "Pop({})".format(self.x)
class PapMember:
def __init__(self):
self.x = random.randint(0, 200)
def __repr__(self):
return "Pap({})".format(self.x)
class EvolutionaryAlgorithm:
def __init__(self, member_type: Any, populationSize: int) -> None:
self.__member_type = member_type
self.__population = [self.__member_type() for _ in range(populationSize)]
#property
def population(self):
return self.__population
class EvolutionaryAlgorithmFactory(Enum):
POP_MEMBER = partial(EvolutionaryAlgorithm, PopMember)
PAP_MEMBER = partial(EvolutionaryAlgorithm, PapMember)
def __call__(self, *args, **kwargs) -> Any:
return self.value(*args, **kwargs)
def __str__(self) -> str:
return self.name
evolution = EvolutionaryAlgorithmFactory.POP_MEMBER(100)
print(evolution.population)
I've created a new class and I'd like to define how to use [] to select things from it. Is there a way to do that?
class NewClass:
def __init__(self, list):
self.list_item = list
# def __indexer__(self, slice_object):
# return list[slice_object]
example = NewClass(range(0, 5))
print example[0:3]
Sure, it's called __getitem__.
class NewClass(object):
def __init__(self, list):
self.list_item = list
def __getitem__(self, slice_object):
return self.list_item[slice_object]
example = NewClass(range(0, 5))
print(example[0:3])
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.
I have a Grid class which I want to access using myGrid[1][2]. I know I can overload the first set of square brackets with the __getitem__() method, but what about the second.
I thought I could achieve this by having a helper class which also implements __getitem__ and then:
class Grid:
def __init__(self)
self.list = A TWO DIMENSIONAL LIST
...
def __getitem__(self, index):
return GridIndexHelper(self, index)
class GridIndexHelper:
def __init__(self, grid, index1):
self.grid = grid
self.index1 = index1
....
def __getitem__(self, index):
return self.grid.list[self.index1][index]
This seems a little too homebrewed... What is the python way to achieve this?
class Grid:
def __init__(self):
self.list = [[1,2], [3,4]]
def __getitem__(self, index):
return self.list[index]
g = Grid();
print g[0]
print g[1]
print g[0][1]
prints
[1, 2]
[3, 4]
2
As far as I know the way anajem mentions is the only way.
example:
class Grid(object):
def __init__(self):
self.list = [[1, 2], [3, 4]]
def __getitem__(self, index):
return self.list[index[0]][index[1]]
if __name__ == '__main__':
mygrid = Grid()
mygrid[1, 1] #How a call would look
Prints:
4
Does not operate exactly as you want it to but does the trick in my eyes.
you could make the index into a tuple:
def getitem(self,indexTuple):
x, y = indexTuple
...
and access the object override:
instance[[2,3]]
or
instance[(2,3)]
This question is quite old, but I'll add my answer anyway for newcomers.
I ran into this need myself, and here's a solution that worked for me:
class Grid:
def __init__(self):
self.matrix = [[0,1,2],[3,4,5],[6,7,8]]
self.second_access = False
def __getitem__(self, key):
if not self.second_access:
self.first_key = key
self.second_access = True
return self
else:
self.second_access = False
return self.matrix[self.first_key][key]
g = Grid()
print(g[1][2]) # 5
print(g[0][1]) # 1
print(g[2][0]) # 6
Notice that this will not work for single access!
So, for example, if you want to use something of the form g[0] to get [0,1,2] it will not work, and instead you'll get nonsensical result (the object itself).