I have a custom class of objects with an assortment of various attributes of different types. I would like to remove duplicates from a list of these objects based on one of these attributes.
Something like this, but actually get a list of the objects rather than a list of the specified attribute.
filteredData = list(set([x.attribute[0] for x in objList]))
You need realize methods hash and eq on object
class A:
def __init__(self, a):
self.attr1 = a
def __hash__(self):
return hash(self.attr1)
def __eq__(self, other):
return self.attr1 == other.attr1
def __repr__(self):
return str(self.attr1)
Example:
l = [A(5), A(4), A(4)]
print list(set(l))
print list(set(l))[0].__class__ # ==> __main__.A. It's a object of class
Related
class NiceClass():
some_value = SomeObject(...)
some_other_value = SomeOtherObject(...)
#classmethod
def get_all_vars(cls):
...
I want get_all_vars() to return [SomeObject(...), SomeOtherObject(...)], or more specifically, the values of the variables in cls.
Solutions tried that didn't work out for me:
return [cls.some_value, cls.some_other_value, ...] (requires listing the variable manually)
subclassing Enum then using list(cls) (requires using some_value.value to access the value elsewhere in the program, also type hinting would be a mess)
namedtuples (nope not touching that subject, heard it was much more complicated than Enum)
[value for key, value in vars(cls).items() if not callable(value) and not key.startswith("__")] (too hacky due to using vars(cls), also for some reason it also includes get_all_vars due to it being a classmethod)
There are two ways. This is a straight answer to your question:
class Foo:
pass
class Bar:
x: int = 1
y: str = 'hello'
z: Foo = Foo()
#classmethod
def get_all(cls):
xs = []
for name, value in vars(cls).items():
if not (name.startswith('__') or isinstance(value, classmethod)):
xs.append(value)
return xs
This is what I suggest:
from dataclasses import dataclass, fields
class Foo:
pass
#dataclass
class Bar:
x: int = 1
y: str = 'hello'
z: Foo = Foo()
#classmethod
def get_defaults(cls):
return [f.default for f in fields(cls)]
#classmethod
def get_all(cls):
return [getattr(cls, f.name) for f in fields(cls)]
results:
Bar.get_defaults() == Bar.get_all()
# True -> [1, 'hello', __main__.Foo]
Bar.x = 10
Bar.get_defaults() == Bar.get_all()
# False -> [1, 'hello', __main__.Foo] != [10, 'hello', __main__.Foo]
You can create a list of values and define individual attributes at the same time with minimal boilerplate.
class NiceClass():
(some_value,
some_other_value,
) = _all_values = [
SomeObject(...),
SomeOtherObject(...)
]
#classmethod
def get_all_vars(cls):
return cls._all_values
The most obvious drawback to this is that it's easy to get the order of names and values out of sync.
Ideally, you might like to do something like
class NiceClass:
_attributes = {
'some_value': SomeObject(...),
'some_other_value': SomeOtherObject(...)
}
#classmethod
def get_all_vars(cls):
return cls._attributes.values()
and have some way to "inject" the contents of _attributes into the class namespace directly. The simplest way to do this is with a mix-in class that defines __init_subclass__:
class AddAttributes:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.__dict__.update(cls._attributes)
class NiceClass(AddAttributes):
# As above
...
This might sound like a https://xyproblem.info/ but my solution might work in the other case as well. You can get the fields of an object by using __dict__ or vars (which is considered more pythonic given: Python dictionary from an object's fields)
You could do something like:
class ClassTest:
def __init__(self):
self.one = 1
self.two = 2
list(vars(ClassTest()).values())
But you will see that it has some limitations. It doesn't recognize the not in self.variable_name defined variables like you have used above. It might help you nonetheless, because you can simply define them in init.
In Python, is there are method of using a reference to an object as a key to a dictionary?
Lets say I have a class as follows:
class Example:
def __init__(self, id):
self.id = id
def __hash__(self):
return hash(self.id)
def __eq__(self, other):
if self.id == other.id:
return True
return False
Then, if I create an instance of my class:
object = Example("My object")
Is there any way in Python to use the object object as a key to a dictionary, WITHOUT defining the __hash__ and __eq__ methods?
As it stands now, if I do:
instances = {object: 1}
the later decide that I want to change the id of object
object.id = "Not my object"
I want the dictionary to "know" I changed the id, which it will not with this implementation, because the key is not a reference to the object itself. Is there any way of getting around this in Python?
Thank you!
Why not just add a method to change the keyname to a dict?
For example:
class Example(dict):
def change(self, name, new_name):
self[new_name] = self.pop(name)
return self
x = Example({'hi':'Hello'})
print(x)
x.change('hi','hello')
print(x)
My goal is to check the similarity of an object using unittesting in python. I have this kind of object
class ImageModel(object):
def __init__(self):
self.data = data #this an array
self.name = name
self.path = path
I have read that if you want to do test the similarity of an array self.assertEqual(arr1,arr2) you have to put .all() after each array. But I have to check the similarity of an object which has an array within it. For my case, it would be:
self.assertEqual(ImageObj1, ImageObj2)
But it always show that those object isn't similar, i assume the problem is at the ImageObj.data
so, is there any way to assert equal an array within an object?
One possibility is to override the __eq__ method of your class. Basically, this is the method that is called when you're using the == operator on an ImageModel instance.
Here is an example:
def __eq__(self, other):
return (
# the two instances must be of the same class
isinstance(other, self.__class__) and
# compare name and path, that's straightforward
self.name == other.name and
self.path == other.path and
# and compare data
len(self.data) == len(other.data) and
all(a == b for a, b in zip(self.data, other.data))
)
I have a class which I want to have all the functions of frozenset but i don't want him to be configurable (by init, frozenset gets iterable).
Additionally, I want him to have the function 'reload' - I am loading static list from a server so the user can not change it (so I don't want the user to think he can change it).
The list on the server can be changed by the admin so I need the reload option.
That's what I hoped for:
class A(frozenset):
def __init__(self, list_id):
super().__init__()
self.list_id = list_id
self.reload()
def reload(self):
#loading staff by self.list_id...
pass
But I didn't find a way to 'add' new staff to the class (I tried to re-init it).
May be I am using the wrong staff so if you have anther way for this it fine (I need the option to compare difference between to difference objects):
a = A(1)
b = A(2)
len(a)
iter(a)
a.difference(b)
May be overloading add and update of set will be good but i don't want to do that (it looks bad in code because there are more update-like functions).
You cannot update the frozenset contents, no; it remains immutable even when subclassed.
You can subclass the collections.abc.Set() Abstract Base Class instead; it models an immutable set too; all you need to do really is implement the methods listed in the Abstract Methods column and the rest is taken care of for you:
from collections.abc import Set
class A(Set):
def __init__(self, list_id):
self.list_id = list_id
self.reload()
def reload(self):
values = get_values(self.list_id)
self._values = frozenset(values)
def __contains__(self, item):
return item in self._values
def __iter__(self):
return iter(self._values)
def __len__(self):
return len(self._values)
Not all methods of the built-in frozenset type are implemented; you can easily supply the missing ones as these are aliases of the operator methods:
def issubset(self, other):
return self <= frozenset(other)
def issuperset(self, other):
return self >= frozenset(other)
def union(self, *others):
res = self
for o in others:
res |= frozenset(o)
return res
def intersection(self, *others):
res = self
for o in others:
res &= frozenset(o)
return res
def difference(self, *others):
res = self
for o in others:
res -= frozenset(o)
return res
def symmetric_difference(self, other):
return self ^ frozenset(other)
I constructed a class:
class Foo (object):
def __init__(self,List):
self.List=List
#property
def numbers(self):
L=[]
for i in self.List:
if i.isdigit():
L.append(i)
return L
#property
def letters(self):
L=[]
for i in self.List:
if i.isalpha():
L.append(i)
return L
>>> inst=Foo(['12','ae','45','bb'])
>>> inst.letters
['ae', 'bb']
>>> inst.numbers
['12', '45']
How can I add attributes so I could do inst.numbers.odd that would return ['45']?
Your numbers property returns a list, so a numbers.odd won't work.
However, you could follow a workflow like:
define a small class Numbers, that would define two properties even and odd
For example, Numbers could take a list as argument of its __init__, the even property would return only the even number of this list [i for i in List if int(i)%2 == 0] (and odd the odd ones)...
create an instance of Numbers in your Foo.numbers property (using your Foo.List to initialize it) and return this instance...
Your Numbers class could directly subclass the builtin list class, as suggested. You could also define it like
class Numbers(object):
def __init__(self,L):
self.L = L
#property
def even(self):
return [i for i in self.L if not int(i)%2]
def __repr__(self):
return repr(self.L)
Here, we returning the representation of Numbers as the representation of its L attribute (a list). Fine and dandy until you want to append something to a Numbers instance, for example: you would have to define a Numb.append method... It might be easier to stick with making Numbers a subclass of list:
class Numbers(list):
#property
def even(self):
...
Edited: corrected the // by a %, because I went too fast and wasn't careful enough
Here's a silly example:
class mylst(list):
#property
def odd(self):
return [ i for i in self if int(i)%2 == 1 ]
class Foo(object):
def __init__(self,lst):
self.lst = list(lst)
#property
def numbers(self):
return mylst( i for i in self.lst if i.isdigit() )
a = Foo(["1","2","3","ab","cd"])
print(a.numbers)
print(a.numbers.odd)
Basically, we just subclass list and add a property odd which returns another list. Since our structure is a subclass of list, it is virtually indistinguishable from the real thing (Horray duck typing!). mylst.odd could even return a new instance of mylst if you wanted to be able to filter it again (e.g. a.numbers.odd.in_fibinocci )