What happens when you call `append` on a list? - python

Firstly, I don't know what the most appropriate title for this question would be. Contender: "how to implement list.append in custom class".
I have a class called Individual. Here's the relevant part of the class:
from itertools import count
class Individual:
ID = count()
def __init__(self, chromosomes):
self.chromosomes = list(chromosomes)
self.id = self.ID.next()
Here's what I want to do with this class:
Suppose I instantiate a new individual with no chromosomes: indiv = Individual([]) and I want to add a chromosome to this individual later on. Currently, I'd have to do:
indiv.chromosomes.append(makeChromosome(params))
Instead, what I would ideally like to do is:
indiv.append(makeChromosome(params))
with the same effect.
So my question is this: when I call append on a list, what really happens under the hood? Is there an __append__ (or __foo__) that gets called? Would implementing that function in my Individual class get me the desired behavior?
I know for instance, that I can implement __contains__ in Individual to enable if foo in indiv functionality. How would I go about enable indiv.append(…) functionality?

.append() is simply a method that takes one argument, and you can easily define one yourself:
def append(self, newitem):
self.chromosomes.append(newitem)
No magic methods required.

Related

Python attribute that holds other existing attributes in a list

I am currently working on a python-sqlite project, and i am novice to both.
I have created a class that has some attributes declared inside the __init__ method. I need another attribute that will be a list or array, that will contain some of the already declared attributes of the class. What i want is my list to contain just a reference of the original attributes.
I need this structure in order to be able to call these attributes together, to iterate on them, but i want to be able to call them separately, too.
At first I tried to create that list attribute inside the __init__ method, after the rest declarations. When I create an instance, however, and change the initial value of one of the attributes, the attribute in the list is not updated.
Then I tried to create that same list attribute inside another method of the class, instead of inside the init, and call it from inside my code, and it did what I wanted to.
Why does the different approach has different results?
Here is the code at both cases:
Case #1
class Tools():
def __init__(self):
self.name = "defaultname"
self.manufacturer = "defaultmanuf"
self.tooldetails = [self.name, self.manufacturer]
def get_details(self):
return self.tooldetails
Case #2
class Tools():
def __init__(self):
self.name = "defaultname"
self.manufacturer = "defaultmanuf"
def _set_detail_list(self):
self.tooldetails = [self.name, self.manufacturer]
def get_details(self):
_set_detail_list()
return self.tooldetails
And when I create an instance:
tool1 = Tools()
tool1.name = 'abc'
tool1.get_details()
The first case gives me ["defaultname", "defaultmanuf"] while the second gives me ["abc","defaultmanuf"].
My question is what is the reason python gives me different output for each case? It seems like I miss something important about how initialization is working..
dir() and other similar functions or magic methods could be able to give me what i want, but i think they are not flexible enough if you want many different lists with different sets of attributes. Unluckily, introspection doesn't work very well with sqlite string-formatted commands..
Plus i am curious of the way python works, which I believe is very important..
Thanks!!
Case #1
When your list is created within __init__, it contains pointers to 2 strings. But the link between name and tooldetails is irrevocably broken once your list is created. If you update name, tooldetails will not dynamically update, unless you tell Python to explicitly update tooldetails with new data.
Case #2
Here you explicitly tell Python to reconstruct tooldetails via the method _set_detail_list, which is called within get_details. You update name and then tell Python to rebuild the tooldetails list. Therefore, if you update name followed by get_details, your list will be updated.

What happens when we edit(append, remove...) a list and can we execute actions each time a list is edited

I would like to know if there is a way to create a list that will execute some actions each time I use the method append(or an other similar method).
I know that I could create a class that inherits from list and overwrite append, remove and all other methods that change content of list but I would like to know if there is an other way.
By comparison, if I want to print 'edited' each time I edit an attribute of an object I will not execute print("edited") in all methods of the class of that object. Instead, I will only overwrite __setattribute__.
I tried to create my own type which inherits of list and overwrite __setattribute__ but that doesn't work. When I use myList.append __setattribute__ isn't called. I would like to know what's realy occured when I use myList.append ? Is there some magic methods called that I could overwrite ?
I know that the question have already been asked there : What happens when you call `append` on a list?. The answer given is just, there is no answer... I hope it's a mistake.
I don't know if there is an answer to my request so I will also explain you why I'm confronted to that problem. Maybe I can search in an other direction to do what I want. I have got a class with several attributes. When an attribute is edited, I want to execute some actions. Like I explain before, to do this I am use to overwrite __setattribute__. That works fine for most of attributes. The problem is lists. If the attribute is used like this : myClass.myListAttr.append(something), __setattribute__ isn't called while the value of the attribute have changed.
The problem would be the same with dictionaries. Methods like pop doesn't call __setattribute__.
If I understand correctly, you would want something like Notify_list that would call some method (argument to the constructor in my implementation) every time a mutating method is called, so you could do something like this:
class Test:
def __init__(self):
self.list = Notify_list(self.list_changed)
def list_changed(self,method):
print("self.list.{} was called!".format(method))
>>> x = Test()
>>> x.list.append(5)
self.list.append was called!
>>> x.list.extend([1,2,3,4])
self.list.extend was called!
>>> x.list[1] = 6
self.list.__setitem__ was called!
>>> x.list
[5, 6, 2, 3, 4]
The most simple implementation of this would be to create a subclass and override every mutating method:
class Notifying_list(list):
__slots__ = ("notify",)
def __init__(self,notifying_method, *args,**kw):
self.notify = notifying_method
list.__init__(self,*args,**kw)
def append(self,*args,**kw):
self.notify("append")
return list.append(self,*args,**kw)
#etc.
This is obviously not very practical, writing the entire definition would be very tedious and very repetitive, so we can create the new subclass dynamically for any given class with functions like the following:
import functools
import types
def notify_wrapper(name,method):
"""wraps a method to call self.notify(name) when called
used by notifying_type"""
#functools.wraps(method)
def wrapper(*args,**kw):
self = args[0]
# use object.__getattribute__ instead of self.notify in
# case __getattribute__ is one of the notifying methods
# in which case self.notify will raise a RecursionError
notify = object.__getattribute__(self, "_Notify__notify")
# I'd think knowing which method was called would be useful
# you may want to change the arguments to the notify method
notify(name)
return method(*args,**kw)
return wrapper
def notifying_type(cls, notifying_methods="all"):
"""creates a subclass of cls that adds an extra function call when calling certain methods
The constructor of the subclass will take a callable as the first argument
and arguments for the original class constructor after that.
The callable will be called every time any of the methods specified in notifying_methods
is called on the object, it is passed the name of the method as the only argument
if notifying_methods is left to the special value 'all' then this uses the function
get_all_possible_method_names to create wrappers for nearly all methods."""
if notifying_methods == "all":
notifying_methods = get_all_possible_method_names(cls)
def init_for_new_cls(self,notify_method,*args,**kw):
self._Notify__notify = notify_method
namespace = {"__init__":init_for_new_cls,
"__slots__":("_Notify__notify",)}
for name in notifying_methods:
method = getattr(cls,name) #if this raises an error then you are trying to wrap a method that doesn't exist
namespace[name] = notify_wrapper(name, method)
# I figured using the type() constructor was easier then using a meta class.
return type("Notify_"+cls.__name__, (cls,), namespace)
unbound_method_or_descriptor = ( types.FunctionType,
type(list.append), #method_descriptor, not in types
type(list.__add__),#method_wrapper, also not in types
)
def get_all_possible_method_names(cls):
"""generates the names of nearly all methods the given class defines
three methods are blacklisted: __init__, __new__, and __getattribute__ for these reasons:
__init__ conflicts with the one defined in notifying_type
__new__ will not be called with a initialized instance, so there will not be a notify method to use
__getattribute__ is fine to override, just really annoying in most cases.
Note that this function may not work correctly in all cases
it was only tested with very simple classes and the builtin list."""
blacklist = ("__init__","__new__","__getattribute__")
for name,attr in vars(cls).items():
if (name not in blacklist and
isinstance(attr, unbound_method_or_descriptor)):
yield name
Once we can use notifying_type creating Notify_list or Notify_dict would be as simple as:
import collections
mutating_list_methods = set(dir(collections.MutableSequence)) - set(dir(collections.Sequence))
Notify_list = notifying_type(list, mutating_list_methods)
mutating_dict_methods = set(dir(collections.MutableMapping)) - set(dir(collections.Mapping))
Notify_dict = notifying_type(dict, mutating_dict_methods)
I have not tested this extensively and it quite possibly contains bugs / unhandled corner cases but I do know it worked correctly with list!

Make parent method return subclassed instance

Say I wish to subclass Python's set to change the difference method like so:
class my_set(set):
def difference(self, ls):
ls.append(1)
return super().difference(ls)
Now assume I also added my_method to my_set and I wish to call it on the set resulting from calling difference:
my_instance = my_set([1,2,3])
my_instance.difference([2,3]).my_method()
The above won't work since the set returned from the difference method won't be of type my_set but of the regular set. What is the pythonic way of going around this without having to convert the returned set each time via my_set(my_instance.difference([2,3])).my_method()?
EDIT
I just realised I could wrap the return set like return my_set(super().difference(ls)). Haven't seen that before that, so not sure if this is the right way of achieving what I want. Care to comment?
One thing you can do, is to create a my_set object from the result of super().difference(ls):
class my_set(set):
def difference(self, ls):
ls.append(1)
return my_set(super().difference(ls)) # <---
def my_method(self):
print("StackOverflow")
my_instance = my_set([1,2,3])
my_instance.difference([2,3]).my_method() # CORRECT

How to create a dynamically updating list of class instances, satisfying a particular condition? (Python)

I want to create a list of class instances that automatically updates itself following a particular condition on the instance attributes.
For example, I have a list of object of my custom class Person() and I want to be able to generate a list that always contains all the married persons, i.e. all persons having the attribute 'MAR_STATUS' equal to 'MARRIED'.
Is this possible at all in Python? I have used a C++ precompiler for microsimulations that had a very handy built-in called "actor_set" which did exactly this. But I have no idea of how it was implemented in C++.
Thank you.
List comprehension:
[person for person in people if person.MAR_STATUS == 'MARRIED']
If you need to assign it to a variable and you want that variable to automatically update on every access, you can put this same code in a lambda, a normal function, or, if your variable is a class member, in a property getter.
It is poor form to have "action at a distance" / mutations / side-effects unless it is very carefully controlled.
That said, imperative language will let you do this, if you really want to, as follows. Here we use python's [property getters and setters]:
MARRIED_SET = set()
def updateMarriedSet(changedPerson):
if hasattr(changedPerson,'married') and changedPerson.married==Person.MARRIED:
MARRIED_SET.add(changedPerson)
else:
MARRIED_SET.discard(changedPerson)
class Person(object):
...
#property
def married(self):
"""The person is married"""
return self._married
#married.setter
def married(self, newStatus):
self._married = newStatus
updateMarriedSet(self)
#married.deleter
def married(self):
del self._married
updateMarriedSet(self)
I can imagine this might, possibly, be useful to ensure accesses to getMarriedPeople() runs in O(1) time rather than amortized O(1) time.
The simple way is to generate the list on the fly e.g., as shown in #sr2222's answer.
As an alternative you could call an arbitrary callback each time MAR_STATUS changes. Use __new__ if Person instances are immutable or make MAR_STATUS a property and call registered callbacks in the setter method (see notifications in traits library for a more complex implementation).

Extending a python list class to perform additional functions on its contents?

I want to create an object which will hold other objects, and call a method on each of the objects it holds. The end goal is to generate a script for a program. Each object contains a command that can be printed with printCommand(), eventually into a file. My original solution was this:
a = ObjectList()
a.appendObject(Object())
b = ObjectList()
b.appendObject(Object())
listOfObjects = [a, b]
for Object in listOfObjects:
Object.printCommand()
I create a list variable, add objects to thw list, then loop over it issuing the command. While this works, I am primarily doing this excersize to teach myself programming, so I want to know if there is a more elegant solution than writing code to append, pop, etc. items to a list in an object. Since list already contains these functions, i was thinking the correct thing to do would be to extend list:
class Object:
def __init__(self):
self.Command = "Script Command"
def printCommand(self):
print(self.Command)
class ObjectList(list):
def printCommand(self):
for Object in self.LISTCONTENTS:
Object.printCommand()
However, I am at a loss as to how I would tell it to iterate over its own contents. What belongs at self.LISTCONTENTS? Where does a list object store its contents, and how do you access them from a method?
You can just iterate over self:
The superclass methods (i.e., __iter__()) are all present. Saying self in the context of a for statement will cause the appropriate methods to be invoked.
class Object:
def __init__(self):
self.Command = "Script Command"
def printCommand(self):
print(self.Command)
class ObjectList(list):
def printCommand(self):
for Object in self:
Object.printCommand()
lst = ObjectList()
lst.append(Object())
lst.append(Object())
lst.printCommand()
The way this works is list implements __iter__ which is called behind the scenes when iterating over an object:
>>> for e in [1,2,3].__iter__():
... print(e)
...
1
2
3
>>> for e in [1,2,3]:
... print(e)
...
1
2
3
(a) Don't ever create a class called Object, that's far too close to object.
(b) There is absolutely no need to inherit from list, and frankly I doubt the need to use your own class at all.
(c) The normal way to invoke a function on every element of a list is to use map. If you need to map with the same function repeatedly, you can use partial to bind that function to map's first argument.
Now go look at the documentation.

Categories

Resources