How to implement chain operations in Python? - 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)

Related

call variable in method within the same class

In this example code:
class new:
def __init__(self, list):
self.list = list
def math(self):
average = sum(self.list)/len(self.list)
total = sum(self.list)
return len(self.list)
def values(self):
return self.list
list = new([1,2,3,4])
is there a way to call the average and total variables in the math method without putting it in the return function (I want the math function to only return the length of the list)? I tried this:
list.math().average
but this gives an error. I want to be able to call the variables in one line. Any suggestions?
You can put them in instance attributes.
class new:
def __init__(self, l):
self.list = l
def math(self):
self.average = sum(self.list)/len(self.list)
self.total = sum(self.list)
return len(self.list)
def values(self):
return self.list
mylist = new([1,2,3,4])
mylist.math()
print(mylist.average, mylist.total)
BTW, don't use list as a variable name, it replaces the built-in function with that name.
try this:
class new:
def __init__(self, list):
self.list = list
def average(self):
return sum(self.list)/len(self.list)
def total(self):
return sum(self.list)
def get_length(self):
return len(self.list)
list = new([1,2,3,4])
list.average()
list.sum()
list.get_length()
You can use the global keyword:
class new:
def __init__(self, list):
self.list = list
def math(self):
global average
global total
average = sum(self.list)/len(self.list)
total = sum(self.list)
return len(self.list)
def values(self):
return self.list

Using __repr__ Python

I would like to be able to run this function without needing to add .elements to the end. For instance, if seta=MySet([1,2,3]) and setb=MySet([1,10,11]), I can run setc=seta.intersection(setb.elements), but not without the .elements. How can I run it without needing to type .elements?
class MySet:
def __init__(self, elements):
self.elements=elements
def intersection(self, other_set):
self.other_set=other_set
new_set = []
for j in other_set:
if j in self.elements:
new_set.append(j)
new_set.sort()
return new_set
Easily, all you have to do is access the .elements in the function. No __repr__ required.
class MySet:
def __init__(self, elements):
self.elements=elements
def intersection(self, setb):
other_set = setb.elements
new_set = []
for j in other_set:
if j in self.elements:
new_set.append(j)
new_set.sort()
return new_set
Make your set an iterable by defining __iter__:
class MySet:
def __init__(self, elements):
self.elements=elements
def intersection(self, other_set):
...
def __iter__(self):
return iter(self.elements)
# Or for implementation hiding, so the iterator type of elements
# isn't exposed:
# yield from self.elements
Now iteration over an instance of MySet seamlessly iterates the elements it contains.
I'd strongly suggest looking at the collections.abc module; you're clearly trying to build a set-like object, and getting the basic behaviors in place is easiest by using collections.abc.Set (or collections.abc.MutableSet) as your base class.

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 can I define selection using [] for a new class?

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

How to inherit and extend a list object in Python?

I am interested in using the python list object, but with slightly altered functionality. In particular, I would like the list to be 1-indexed instead of 0-indexed. E.g.:
>> mylist = MyList()
>> mylist.extend([1,2,3,4,5])
>> print mylist[1]
output should be: 1
But when I changed the __getitem__() and __setitem__() methods to do this, I was getting a RuntimeError: maximum recursion depth exceeded error. I tinkered around with these methods a lot but this is basically what I had in there:
class MyList(list):
def __getitem__(self, key):
return self[key-1]
def __setitem__(self, key, item):
self[key-1] = item
I guess the problem is that self[key-1] is itself calling the same method it's defining. If so, how do I make it use the list() method instead of the MyList() method? I tried using super[key-1] instead of self[key-1] but that resulted in the complaint TypeError: 'type' object is unsubscriptable
Any ideas? Also if you could point me at a good tutorial for this that'd be great!
Thanks!
Use the super() function to call the method of the base class, or invoke the method directly:
class MyList(list):
def __getitem__(self, key):
return list.__getitem__(self, key-1)
or
class MyList(list):
def __getitem__(self, key):
return super(MyList, self).__getitem__(key-1)
However, this will not change the behavior of other list methods. For example, index remains unchanged, which can lead to unexpected results:
numbers = MyList()
numbers.append("one")
numbers.append("two")
print numbers.index('one')
>>> 1
print numbers[numbers.index('one')]
>>> 'two'
Instead, subclass integer using the same method to define all numbers to be minus one from what you set them to. Voila.
Sorry, I had to. It's like the joke about Microsoft defining dark as the standard.
You can avoid violating the Liskov Substitution principle by creating a class that inherits from collections.MutableSequence, which is an abstract class. It would look something like this:
def indexing_decorator(func):
def decorated(self, index, *args):
if index == 0:
raise IndexError('Indices start from 1')
elif index > 0:
index -= 1
return func(self, index, *args)
return decorated
class MyList(collections.MutableSequence):
def __init__(self):
self._inner_list = list()
def __len__(self):
return len(self._inner_list)
#indexing_decorator
def __delitem__(self, index):
self._inner_list.__delitem__(index)
#indexing_decorator
def insert(self, index, value):
self._inner_list.insert(index, value)
#indexing_decorator
def __setitem__(self, index, value):
self._inner_list.__setitem__(index, value)
#indexing_decorator
def __getitem__(self, index):
return self._inner_list.__getitem__(index)
def append(self, value):
self.insert(len(self) + 1, value)
class ListExt(list):
def extendX(self, l):
if l:
self.extend(l)

Categories