How can I define selection using [] for a new class? - python

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])

Related

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]

Can class instances be accessed via an index in python?

Consider for example that we have a class 'Agent' as below:
class Agent:
def __init__(self, number):
self.position = []
self.number = number
for i in range(number):
self.position.append([0, 0])
I can make an instance of the class by:
agent = Agent(10)
and then access the i'th agent's position by:
agent.position[i]
However, this does not seem elegant enough and to me it's a bit counter-intuitive. Instead I want to index the class instance itself. For example:
pos_i = agent[i].position
which should return the same answer as the one-line code above. Is there a way to accomplish this?
If you want to do that, you just need a class-level container, with all instances.
Since your positions, given your example, are created in an arbitrary order, I'd suggest using a dictionary.
You can just fill the class-level "position" dictionary. You could then just implement the __getitem__ method to retrieve elements from this dictionary:
class Agent:
position = {}
def __new__(cls, pos):
if pos in cls.position:
return cls.position[pos]
instance = super().__new__(cls)
cls.position[pos] = instance
return instance
def __getitem__(self, item):
return self.position[pos]
This, however, will only allow you to retrieve an instance given the position from an instance - i.e.:
agent_5 = Agent(5)
agent_10 = agent_5[10]
would work, but not:
agent_10 = Agent[10]
If you want that, you have to use a custom metaclass, and put the __getitem__ method there:
class MAgent(type):
def __getitem__(cls, item):
return cls.position[pos]
class Agent(metaclass=MAgent):
position = {}
def __new__(cls, pos):
if pos in cls.position:
return cls.position[pos]
instance = super().__new__(cls)
cls.position[pos] = instance
return instance
If you want to overload the indexing operator just overload the __getitem__ method in the class.
class Agent:
def __getitem__(self, key):
return self.position[key]
>>> myobj = MyClass()
>>> myobj[3]

How to implement chain operations in Python?

class Array:
def __init__(self):
self.list = []
def add(self, num):
self.list.append(num)
a = Array()
a.add(1).add(2)
I would like to add number 1, 2 to self.list like this.
How can I implement?
After your insertion returns the instance itself for second operation, then you will have instance itself so you can perform add operation:
def add(self, num):
self.list.append(num)
return self
Return the object itself
def add(self, num):
self.list.append(num)
return self
As an alternative approach, why not just let your add method take a list of values as input? Seems like it would be easier to use like that
def add(self, vals):
self.list += vals
So now you can
a.add([1,2])
Instead of
a.add(1).add(2)

Python class structure

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.

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