Python custom list prefilled with static elements - python

Question
In python 2.7, I want to create a custom list that extends the python list by prefilling it with some static elements. I also want to extent the python list by adding some custom methods (i.e. filtering, re-initialization, etc...).
For example:
my_list = FitFuctionsList()
should give me a list already filled with some fixed elements.
I tried to inherit from the python list:
class FitFuctionsList(list):
def __init__(self):
list.__init__(['some', 'fixed', 'list'])
but the initialization fails (an empty list is returned).
Suggestions on alternative approaches are also welcome.
Solution summary
nachshon provided a working solution even though he does not explain why it works (and why the previous example did not):
class FitFuctionsList(list):
def __init__(self):
super(FitFuctionsList, self).__init__(['some', 'fixed', 'list'])
If the need is only to initialize a list with fixed values (no custom methods), Claudiu provided a clever way of using an helper function with an attribute to initialize the list.
This methods is elegant and robust since it avoids using globals:
def FitFuctionsList():
return list(FitFuctionsList.prefilled_elements)
FitFuctionsList.prefilled_elements = ["a", "b", "rofl"]

You can inherit from list like this:
class FitFunctionsList(list):
def __init__(self, *args, **kwargs):
super(FitFunctionsList, self).__init__(['some','default','values'])
def my_custom_filter(self, criteria):
pass`
that will initialize the list with default values, you can treat it as a list and you can add custom methods.
In response to #user2304916 comment on this answer:
list.__init__(self, ['some','default','values'])
should work but super returns an object that will allow you to access all inherited stuff, in the case of multiple inheritence it will give you access to all metods in the correct order. it also does not require you specify which class that you inherit from you call a parent methos.

It seems the simplest solution here would be a helper function, something like...
prefilled_elements = ["a", "b", "rofl"]
def FitFuctionsList():
return list(prefilled_elements)
EDIT: and you could do the following if you didn't want prefilled_elements to be a global:
def FitFuctionsList():
return list(FitFuctionsList.prefilled_elements)
FitFuctionsList.prefilled_elements = ["a", "b", "rofl"]
But as to your updated question of having custom methods, you'll have to subclass list as nachson shows.

You could do something along these lines:
class FitFunctionList(list):
def __init__(self, **kwargs):
if 'func' in kwargs:
self.func=kwargs['func']
else:
self.func=range
if 'args' in kwargs:
self.args=kwargs['args']
else:
self.args=10
super(FitFunctionList, self).__init__(self.func(self.args))
def test_func(arg):
return [i*i for i in range(arg)]
print FitFunctionList()
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print FitFunctionList(func=test_func, args=6)
# [0, 1, 4, 9, 16, 25]

Related

Is there a custom way for implementing an object clone function?

I am currently using something like this:
class MyClass:
def __init__(self, myVar):
self.myVar = myVar
def clone(self):
return MyClass(self.myVar)
Is there a more custom (standard) way of doing this in Python, perhaps by overriding operator __new__ or something of that sort?
I'd rather this function to be an instance function and not a class (static) function, but I'd be happy to hear any suggestion.
Thanks for helping out.
A standardised way of doing this in Python is for the caller to call copy.copy(obj) or copy.deepcopy(obj).
Documentation at docs.python.org
As standard, copy() will result in the copied object having references to the same objects that the original did. deepcopy() will result in the copied object having references to copies of those objects.
To illustrate:
class Example:
def __init__(self, vals):
self.vals = vals
import copy
orig = Example([1, 2, 3])
shallow = copy.copy(orig)
deep = copy.deepcopy(orig)
orig.vals.append('surprise')
print(f'orig.vals: {orig.vals}')
print(f'shallow.vals: {shallow.vals}')
print(f'deep.vals: {deep.vals}')
results in:
orig.vals: [1, 2, 3, 'surprise']
shallow.vals: [1, 2, 3, 'surprise']
deep.vals: [1, 2, 3]
Note the difference between the shallow and the deep copy. shallow.vals still pointed to the list in orig.vals, whereas deep.vals was an independent copy of that list.
Now, for your example class, this is all that is needed: you don't need to add a special method to your class. But if you wanted cloning behaviour that was more custom, you could implement the __copy__ and/or __deepcopy__ methods in your class.

How to assign to a base class object when inheriting from builtin objects?

I'm trying to extend the Python built-in list object to include metadata. I would like to be able to assign by reference to the base class object in some cases for efficiency.
For example:
class meta(list):
def __init__(self, data=None, metadata=None):
if data is not None:
super().__init__(data) # performs a copy of data
else:
super().__init__()
self.metadata = metadata
def foo(self):
new_data = [ i for i in range(10) ]
return meta(new_data, "my meta data")
This works as expected. The call to foo returns a new meta object with the values 0-9 in it. However, the list created and assigned to new_data in the list comprehension is copied inside the initializer of meta due to the call to the base class initializer. This additional copy is unnecessary as it could simply assign the reference of new_data to the inherited list object as no other references to it could exist after exiting foo.
What I'm trying to describe is something like this:
class meta(list):
def __init__(self, data=None, metadata=None):
if data is not None:
super().__init__(data) # performs a copy of data
else:
super().__init__()
self.metadata = metadata
def foo(self):
result = meta(None, "my meta data") # an efficient initialization
new_data = [ i for i in range(10) ]
super(list, result) = new_data # just assign the reference instead of copying
return result
But I know this is not correct syntactically. However, it does describe what I'm trying to accomplish. The intent is the new_data list object would simply be referred to by the new meta object via a reference to it being assigned to the underlying list object.
I know I could use a list member object instead of inheriting from list but that causes other inefficiencies because now all of the list attributes have to be defined in the meta class and would get wrapped in another layer of function calls.
So…my questions are:
Is there a way to do this at all?
Can I access the underlying object as an independent object from the subclass?
Can it be implemented cleanly without creating more overhead than I'm trying to remove?
Is there some obscure __assign__ method available that isn't an undocumented 'feature' of the language?
The instance of meta is, necessarily, a new instance, and you assign to names, not objects. You can't simply replace the new instance with an instance of list. That is, the new instance of meta doesn't contain a reference to another list instance; it is the list instance, just with a __class__ attribute that refers to meta, not list.
If you don't want to make a copy of the argument, don't make a list argument in the first place.
class meta(list):
def __init__(self, data=None, metadata=None):
if data is not None:
super().__init__(data) # performs a copy of data
else:
super().__init__()
self.metadata = metadata
def foo(self):
new_data = range(10)
return meta(new_data, "my meta data")
list.__init__ isn't exepecting a list; it's just expecting an iterable, references to the elements of which can be added to the just-constructed list.
You would probably want to override __new__ anyway, since list.__new__ already contains the logic that decides if there is an iterable available to pass to __init__.
class meta(list):
def __new__(cls, *args, metadata=None, **kwargs):
new_list = super().__new__(cls, *args, **kwargs)
new_list.metadata = metadata
return new_list
def foo(self):
return meta(range(10), metadata="my meta data")
(And finally, foo should probably be a static method or a class method, since it makes no use of the meta instance that invokes it.)
Although #chepner didn't answer my question directly, I got some encouragement from them and came up with the following solution.
Consider having two meta objects and we want to add the underlying list objects together element by element.
The most straight forward answer is similar to my original post. I'll leave the error checking to the reader to implement.
class meta(list):
def __init__(self, data, metadata):
super().__init__(data)
self.metadata = metadata
def __add__(self, other):
return meta([self[i] + other[i] for i in range(len(self))], self.__metadata)
a = meta([1, 2, 3, 4, 5], "meta data")
b = meta([6, 7, 8, 9, 0], "more data")
a + b
[7, 9, 11, 13, 5]
Note: The meta data is just there to complete the example and I know I left out checks for making sure the b list isn't shorter than the a list. However, the operation works as expected. It's also easy to see the list created by the list comprehension is later copied completely in meta.__init__ at the call to list.__init__ via the super().__init__(...) call. This second copy is what I wanted to avoid.
Thanks to #chepner's identification of list.__init__ taking an iterable, I came up with the following solution.
class meta_add_iterable:
def __init__(self, *data):
self.data = data # copies only the references to the tuple of arguments passed in
self.current = -1
def __iter__(self):
return self
def __next__(self):
self.current += 1
if self.current < len(self.data[0]):
return sum([ i[self.current] for i in self.data ])
raise StopIteration
class meta(list):
def __init__(self, data, metadata):
super().__init__(data)
self.metadata = metadata
def __add__(self, other):
return meta(meta_add_iterable(self, other), self.metadata)
a = meta([1, 2, 3, 4, 5], "meta data")
b = meta([6, 7, 8, 9, 0], "more data")
a + b
[7, 9, 11, 13, 5]
In the above implementation, there is no interim list created via list comprehension in the meta.__add__ method. Instead, we simply pass the two meta objects (self and other) to the iterator object. Here the only copy that is done is to copy the references to the original meta objects so the iterator can refer to them. Then the call to meta.__init__ passes the iterator in instead of an already created list. The list.__init__ method simply creates the list from this iterator which means each referred to meta object is accessed only once to retrieve its data and the result is only written once in the list.__init__ method. The secondary copy is completely elided in this implementation because the add operation is actually deferred until the iterators __next__ method is called in list.__init__.
The best part is we don't need to check if we are initializing from a list object or an iterator!
I know there are plenty of things that can go wrong as it stands. I left out all of the error checking and such so just the process was visible to the reader.
I'm not sure if implementing it using the __new__ method would still be better, as suggested by #chepner. Personally, I can't see the benefit. Maybe #chepner can expand on why that may still be the better solution. Either way, this seems to have answered my question and I'm hopeful it may help others.

Returning dictionary representation of object when class attributes are private (Python)

I am trying to generate a dictionary from an object in Python. I have used object.__dict__ which works when the class attributes are not set to private. However when I set the attributes to private, the dictionary keys change from 'attribute' to '_ClassName__attribute'
I'm just wondering if there's a built in function to keep the attribute name as just 'length' (to give an example), or if I would have to do something else.
class Rectangle():
def __init__(self,length,breadth):
self.__length = length
self.__breadth = breadth
def get_length(self):
return self.__length
def get_breadth(self):
return self.__breadth
r = Rectangle(2,4)
print(r.__dict__)
Output:
{'_Rectangle__length': 2, '_Rectangle__breadth': 4}
Desired Output:
{'length': 2, 'breadth': 4}
EDIT: Should note that my code is a bit more complex than this, and due to inheritance, some of my dictionaries may look something like this:
{'_Rectangle__length' : 2, '_Circle__radius' : 5, '_Square__height' : 9}
As such, if there is no built-in function to do what I'm looking for simply, I may need to to remove everything up to and including __ for all keys. I've already tried this, but had issues with not being able to update a dictionary during iteration. Any suggestions would be appreciated.
A Pythonic design would be
class Rectangle:
def __init__(self, length, breadth):
self._length = length
self._breadth = breadth
#property
def length(self):
return self._length
#property
def breadth(self):
return self._breadth
def to_dict(self):
return {'length': self._length, 'breadth': self._breadth}
r = Rectangle(2, 4)
print(r.to_dict())
Use _-prefixed names to indicate attributes that should not be used directly, and use property to provide read-only access to such attributes. Define a method that produces the desired dict explicitly, rather than trying to reuse __dict__ directly.

I want the properties of __list__?

I have a class successors that contains a list of successors. [1, 2, 3, 4]
node_successors = Successors()
When the class is assigned to a variable, for example:
neigbour = node_successors
I want neighbour to be set to the list I have stored within the class (without having to call get_successor_list() for example)
Similar to:
def __str__(self):
return "This class can be used as a string now"
Maybe will be useful for you to read this section in Python3 reference docs: Emulating container types
With your example it seems you want to get a list from your object (not the properties), you would do:
If you want to extract a list from your object, you can implement the iterator protocol (__iter__()). Then you can do list(yourInstance)
Alternatively you can implement bound indexing (__len__() and __getitem__()) either.
neighbour = node_successors.get_successor_list() if this function returns a list.
you can make the list inside the class global.
class SomeClass:
global some_list
some_list = []
some_list.append('x')
print(some_list)

Python class that automatically appends values to existing attribute OR creates and fills new attribute

My goal here is to be able to create nested dictionaries that have attributes that hold lists of values. For example, I want to be able to do something like this:
mydict['Person 1']['height'].vals = [23, 25, 32]
mydict['Person 2']['weight'].vals = [100, 105, 110]
mydict['Person 2']['weight'].excel_locs ['A1', 'A2', 'A3']
So, for each "person" I can keep track of multiple things I might have data on, such as height and weight. The attribute I'm calling 'vals' is just a list of values for heights or weights. Importantly, I want to be able to keep track of things like where the raw data came from, such as its location in an Excel spreadsheet.
Here's what I am currently working off of:
import collections
class Vals:
def add(self, list_of_vals=[], attr_name=[]):
setattr(self, attr_name, val)
def __str__(self):
return str(self.__dict__)
mydict = collections.defaultdict(Vals)
So, I want to be able to add new keys as needed, such as mydict['Person 10']['test scores'], and then create a new attribute such as "vals" if it doesn't exist, but also append new values to it if it does.
Example of what I want to achieve:
mydict['Person 10']['test scores'].add([10, 20, 30], 'vals')
Which should allow mydictmydict['Person 10']['test scores'].vals to return [10, 20, 30]
But then I also want to be able to append to this list later on if needed, such that using .add again append to the existing list. For example, mydict['Person 10']['test scores'].add([1, 2, 3], 'vals') should then allow me to return [10, 20, 30, 1, 2, 3] from mydict['Person 10']['test scores'].vals.
I'm still very much getting used to object oriented programming, classes, etc. I am very open to better strategies that might exist for achieving my goal, which is just a nested dictionary structure which I find convenient for holding data.
If we just modify the Vals class above, it needs a way to determine whether an attribute exists. If so, create and populate it with list_of_vals, otherwise append to the existing list
Thanks!
from what I understand, you want something that you can conveniently hold data. I would actually build a class instead of a nested dictionary, because this allows for an easier way to see how everything works together (and it also helps organize everything!).
class Person(object):
"""__init__() functions as the class constructor"""
def __init__(self, name=None, testScores=None):
self.name = name
self.testScores = testScores
# make a list of class Person(s)
personList = []
personList.append(Person("Person 1",[10,25,32]))
personList.append(Person("Person 2",[22,37,45]))
print("Show one particular item:")
print(personList[0].testScores)
personList[0].testScores.append(50)
print(personList[0].testScores)
print(personList[1].name)
Basically, the Person class is what holds all of the data for an instance of it. If you want to add different types of data, you would add a parameter to the init() function like this:
def __init__(self, name=None, testScores=None, weight = None):
self.name = name
self.testScores = testScores
self.weight = weight
You can edit the values just like you would a variable.
If this isn't what you are looking for, or you are confused, I am willing to try to help you more.
I agree that using a Person class is a better solution here. It's a more abstract & intuitive way to represent a the concept, which will make your code easier to work with.
Check this out:
class Person():
# Define a custom method for retrieving attributes
def __getattr__(self, attr_name):
# If the attribute exists, setdefault will return it.
# If it doesn't yet exist, it will set it to an empty
# dictionary, and then return it.
return self.__dict__.setdefault(attr_name, {})
carolyn = Person()
carolyn.name["value"] = "Carolyn"
carolyn.name["excel_loc"] = "A1"
print(carolyn.name)
# {"value": "Carolyn", "excel_loc": "A1"}
maria = Person()
print(maria.name)
# {}
Then collecting people into a dictionary is easy:
people = {
"carolyn": carolyn,
"maria": maria
}
people["Ralph"] = Person()
people["Ralph"].name["value"] = "Ralph"
You've also made a tricky mistake in defining the add method:
def add(self, list_of_vals=[], attr_name=[]):
...
In Python, you never want to set an empty list as a default variable. Because of the way they're stored under the hood, your default variable will reference the same list every time, instead of creating a new, empty list each time.
Here's a common workaround:
def add(self, list_of_vals=None, attr_name=None):
list_of_vals = list_of_vals or []
attr_name = attr_name or []
...

Categories

Resources