Python - Access a class from a list using a key - python

Is there any way to make a list of classes behave like a set in python?
Basically, I'm working on a piece of software that does some involved string comparison, and I have a custom class for handling the strings. Therefore, there is an instance of the class for each string.
As a result, I have a large list containing all these classes. I would like to be able to access them like list[key], where in this case, the key is a string the class is based off of (note: the string will never change once the class is instantiated, so it should be hashable).
It seems to me that I should be able to do this somewhat easily, by adding something like __cmp__ to the class, but either I'm being obtuse (likely), or I'm missing something in the docs.
Basically, I want to be able to do something like this (Python prompt example):
>>class a:
... def __init__(self, x):
... self.var = x
...
>>> from test import a
>>> cl = set([a("Hello"), a("World"), a("Pie")])
>>> print cl
set([<test.a instance at 0x00C866C0>, <test.a instance at 0x00C866E8>, <test.a instance at 0x00C86710>])
>>> cl["World"]
<test.a instance at 0x00C866E8>
Thanks!
Edit Some additional Tweaks:
class a:
... def __init__(self, x):
... self.var = x
... def __hash__(self):
... return hash(self.var)
...
>>> v = a("Hello")
>>> x = {}
>>> x[v]=v
>>> x["Hello"]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Hello'
>>> x["Hello"]

Just write a class that behaves a bit like a mapping:
class ClassDict(object):
def __init__(self):
self.classes = {}
def add(self, cls):
self.classes[cls.__name__] = cls
def remove(self, cls):
if self.classes[cls.__name__] == cls:
del self.classes[cls.__name__]
else:
raise KeyError('%r' % cls)
def __getitem__(self, key):
return self.classes[key]
def __repr__(self):
return 'ClassDict(%s)' % (', '.join(self.classes),)
class C(object):
pass
class D(object):
pass
cd = ClassDict()
cd.add(C)
cd.add(D)
print cd
print cd['C']

Why don't you just do:
>>> v = MyStr("Hello")
>>> x = {}
>>> x[v.val]=v
>>> x["Hello"]
MyStr("Hello")
Why go through all the trouble of trying to create a hand-rolled dict that uses different keys than the ones you pass in? (i.e. "Hello" instead of MyStr("Hello")).
ex.
class MyStr(object):
def __init__(self, val):
self.val = str(val)
def __hash__(self):
return hash(self.val)
def __str__(self):
return self.val
def __repr__(self):
return 'MyStr("%s")' % self.val
>>> v = MyStr("Hello")
>>> x = {}
>>> x[str(v)]=v
>>> x["Hello"]
MyStr("Hello")

Set and dict use the value returned by an object's __hash__ method to look up the object, so this will do what you want:
>>class a:
... def __init__(self, x):
... self.var = x
...
... def __hash__(self):
... return hash(self.var)

As I remember "set" and "dict" uses also __hash__
From Python 2.x doc:
A dictionary’s keys are almost arbitrary values. Values that are not hashable, that is, values containing lists, dictionaries or other mutable types (that are compared by value rather than by object identity) may not be used as keys.

Do you want something like this
class A(object):
ALL_INSTANCES = {}
def __init__(self, text):
self.text = text
self.ALL_INSTANCES[self.text] = self
a1 = A("hello")
a2 = A("world")
print A.ALL_INSTANCES["hello"]
output:
<__main__.A object at 0x00B7EA50>

Related

Use __str__ of each instance attribute for a class's __str__

Is there a way in Python 3 to recursively call private member variables unique __str__ functions programmatically? Something like:
class A:
def __str__(self):
return "A"
class B:
def __str__(self):
return "B"
class C:
def __init__(self):
self._A = A()
self._B = B()
def __str__(self):
for var in vars(self):
return str(var)
When calling the individual private members, it works fine. Would like a method to do it dynamically however.
Thanks so much.
The vars function returns a dictionary where the keys are the variable names (as strings) and the values are the values of the variable. So iterating over the values should work.
class A:
def __str__(self):
return "A"
class B:
def __str__(self):
return "B"
class C:
def __init__(self):
self._A = A()
self._B = B()
def __str__(self):
output = ""
for _,var in vars(self).items(): #Iterate over the values
output += str(var) #Use the str() function here to make the object return its string form
return output #Need to return instead of print since that is what the __str__() function should do
You can add some kind of separator (like a \n) between the values if you want. Just replace str(var) with str(var) + "\n".
You can also use the dictionary keys; vars() is self.__dict__:
>>> class A:
... def __str__(self):
... return self.__class__.__name__
...
>>> class B:
... def __str__(self):
... return self.__class__.__name__
...
>>> str(A())
'A'
>>> repr(A()) # "long-form" the hex-string is id()
'<__main__.A object at 0x10f65a908>'
>>> class C:
... def __init__(self):
... self.A = A()
... self.B = B()
... def __str__(self):
... return '\n'.join(self.__dict__)
...
>>> C()
<__main__.C object at 0x10f65aa58>
>>> print(C()) # Uses str(C())
A
B
vars(self) is effectively self. In turn, self.__dict__ is a dict used to store an object’s (writable) attributes.
>>> C().__dict__
{'A': <__main__.A object at 0x10f65aa90>, 'B': <__main__.B object at 0x10f65aac8>}
The signature is '\n'.join(iterable), and when you iterate over a dictionary, you iterate over its keys, which suffices in this case.
Note on Dataclasses
I'm not totally sure if (Python 3.7+) dataclasses are an easier solution here. That's because they automatically implement a __repr__() but not a __str__() as far as I can tell:
>>> from dataclasses import dataclass
>>>
>>> #dataclass
... class C:
... _A: object = A()
... _B: object = B()
...
>>> c = C() # still uses repr() for each field
>>> str(c)
'C(_A=<__main__.A object at 0x10f373828>, _B=<__main__.B object at 0x10f373940>)'
In other words, you'd need to replace A.__str__ with A.__repr__ (same for B, which is maybe not something you want to do in the first place with regards to those two classes.)
Are you sure you shouldn't be using __repr__?
Anyway, here's an example using attrs, beacuse I'm stuck on Python 3.5. With dataclasses it will work in a similar manner.
import attr
class A:
def __str__(self):
return 'A'
class B:
def __str__(self):
return 'B'
#attr.s
class C:
a = attr.ib(default=attr.Factory(A))
b = attr.ib(default=attr.Factory(B))
if __name__ == '__main__':
c = C()
print(c) # __str__ defaults to __repr__
def __str__(self):
bits = ['<C']
for a in self.__attrs_attrs__:
bits.append(' %s=%s' % (a.name, getattr(self, a.name)))
bits.append('>')
return ''.join(bits)
C.__str__ = __str__
print(c) # custom __str__

python class behaves like dictionary-or-list-data-like

In python3 console, input those:
>>> import sys
>>> sys.version_info
sys.version_info(major=3, minor=4, micro=3, releaselevel='final', serial=0)
>>> type(sys.version_info) # this is class type
<class 'sys.version_info'>
>>> sys.version_info[0:2] # ?? But it acts like a list-data-like
(3, 4)
My questions are:
How can a class act like dictionary-or-list-data-like?
May give an example to construct a class like this?
Is there some documentation about
this?
Python contains several methods for emulating container types such as dictionaries and lists.
In particular, consider the following class:
class MyDict(object):
def __getitem__(self, key):
# Called for getting obj[key]
def __setitem__(self, key, value):
# Called for setting obj[key] = value
If you write
obj = MyDict()
Then
obj[3]
will call the first method, and
obj[3] = 'foo'
will call the second method.
If you further want to support
len(obj)
then you just need to add the method
def __len__(self):
# Return here the logical length
Here is an example of a (very inefficient) dictionary implemented by a list
class MyDict(object):
def __init__(self, seq=None):
self._vals = list(seq) if seq is not None else []
def __getitem__(self, key):
return [v[1] for v in self._vals if v[0] == key][0]
def __setitem__(self, key, val):
self._vals = [v for v in self._vals if v[0] != key]
self._vals.append((key, val))
def __len__(self):
return len(self._vals)
You can use it pretty much like a regular dict:
obj = MyDict()
obj[2] = 'b'
>>> obj[2]
'b'
It's quite easy ... All you need to do is define a __getitem__ method that handles slicing or integer/string lookup. You can do pretty much whatever you want...
class Foo(object):
def __init__(self, bar, baz):
self.bar = bar
self.baz = baz
def __getitem__(self, ix):
return (self.bar, self.baz).__getitem__(ix)
Here's a cheat sheet of what will be passed to __getitem__ as ix in the following situations:
f[1] # f.__getitem__(1)
f[1:] # f.__getitem__(slice(1, None, None))
f[1:, 2] # f.__getitem__( (slice(1, None, None), 2) )
f[1, 2] # f.__getitem__( (1, 2) )
f[(1, 2)] # f.__getitem__( (1, 2) )
The trick (which can be slightly non-trivial) is simply writing __getitem__ so that it looks at the type of the object that was passed and then does the right thing. For my answer, I cheated by creating a tuple in my __getitem__ and then I called __getitem__ on the tuple (since it already does the right thing in all of the cases that I wanted to support)
Here's some example usage:
>>> f = Foo(1, 2)
>>> f[1]
2
>>> f[0]
1
>>> f[:]
(1, 2)
note that you don't typically need to even do this yourself. You can create a named tuple to do the job for you:
from collections import namedtuple
Foo = namedtuple('Foo', 'bar, baz')
And usage is pretty much the same:
>>> f = Foo(1, 2)
>>> f[1]
2
>>> f[0]
1
>>> f[:]
(1, 2)
The main difference here is that our namedtuple is immutable. Once created, we can't change it's members.
i think in python like ECMAScript (aka javascript) class is a dictionary or associative array(associative array). since you can add a property or method to your class at runtime.(see)
class A(object):
def __init__(self):
self.x = 0
a = A()
a.y=5
print a.y # 5
if you want write a class like that you can use __getitem__ and __setitem__ methods:
class A(object):
class B(object):
def __init__(self, x, y):
self.vals = (x, y)
def __getitem__(self, key):
return self.vals[key]
def __setitem__(self, key, val):
self.vals[key] = val
def __len__(self):
return len(self.__vals)
def __init__(self, x, y):
self.b = self.B(x,y)
a = A('foo','baz')
print type(a.b) # __main__.b __main__ because we run script straightly
print a.b[:] # ('foo', 'baz')
You can achieve the same behaviour by overriding getitem() and setitem() in your class.
class Example:
def __getitem__(self, index):
return index ** 2
>>> X = Example()
>>> X[2]
>>> 4
You can override setitem() too in your class for achieving the setter kind of thing.

python reference a property like a function

How do you pythonically set multiple properties without referencing them individually? Below is my solution.
class Some_Class(object):
def __init__(self):
def init_property1(value): self.prop1 = value
def init_property2(value): self.prop2 = value
self.func_list = [init_property1, init_property2]
#property
def prop1(self):
return 'hey im the first property'
#prop1.setter
def prop1(self, value):
print value
#property
def prop2(self):
return 'hey im the second property'
#prop2.setter
def prop2(self, value):
print value
class Some_Other_Class(object):
def __init__(self):
myvalues = ['1 was set by a nested func','2 was set by a nested func']
some_class= Some_Class()
# now I simply set the properties without dealing with them individually
# this assumes I know how they are ordered (in the list)
# if necessary, I could use a map
for idx, func in enumerate(some_class.func_list):
func(myvalues[idx])
some_class.prop1 = 'actually i want to change the first property later on'
if __name__ == '__main__':
test = Some_Other_Class()
this became necessary to do when I had many many properties to initialize with user defined values. My code otherwise would look like a giant list of setting each property individually (very messy).
Note that many people have good answers below and I think I have reached a good solution. This is a re-edit mostly trying to clearly state the question. But, if anyone has a better approach please share!
just use the #property decorator
>>> class A:
... a=2
... #property
... def my_val(self,val=None):
... if val == None:return self.a
... self.a = val
...
>>> a=A()
>>> a.my_val
2
>>> a.my_val=7
>>> a.my_val
7
something like this?
if you only want to allow setting then dont give it a default val
>>> class A:
... a=2
... #property
... def my_val(self,val):
... self.a = val
...
>>> a=A()
>>> a.my_val
<Exception>
>>> a.my_val=7
>>> a.a
7
or if you only want to allow retrieval just ommit the 2nd arg
>>> class A:
... a=2
... #property
... def my_val(self):
... return self.a
...
...
>>> a=A()
>>> a.my_val
2
>>> a.my_val=7
<Exception>
I ... finally think I know what you're trying to do, and you don't need to do it the way you're approaching it. Let me take a stab at this.
class someclass(object):
def __init__(self):
func_list = [self.setter1, self.setter2]
value_list = [1, 2]
# These lines don't need to be this complicated.
# for ind in range(len(func_list)):
# func_list[ind](value_list[ind])
for idx, func in enumerate(func_list):
func(value_list[idx])
# Or even better
for idx, (func, val) in enumerate(zip(func_list, value_list)):
func(val)
def setter1(self, value):
self.a = value
def setter2(self, value):
self.b = value
It's worth pointing out that the idx variable and enumerate calls are superfluous in the second form, but I wasn't sure if you need that elsewhere.
If you look up the property in the object dict, you will get the property descriptor (if any), and likewise with the class; e.g.
a = SomeClass()
descriptor = a.__dict__.get('descriptor', type(a).__dict__.get('descriptor'))
Assuming that descriptor is a descriptor, it will have the following methods:
['deleter', 'fdel', 'fget', 'fset', 'getter', 'setter']
Note the fget and fset.

Python: Make class iterable

I have inherited a project with many large classes constituent of nothing but class objects (integers, strings, etc). I'd like to be able to check if an attribute is present without needed to define a list of attributes manually.
Is it possible to make a python class iterable itself using the standard syntax? That is, I'd like to be able to iterate over all of a class's attributes using for attr in Foo: (or even if attr in Foo) without needing to create an instance of the class first. I think I can do this by defining __iter__, but so far I haven't quite managed what I'm looking for.
I've achieved some of what I want by adding an __iter__ method like so:
class Foo:
bar = "bar"
baz = 1
#staticmethod
def __iter__():
return iter([attr for attr in dir(Foo) if attr[:2] != "__"])
However, this does not quite accomplish what I'm looking for:
>>> for x in Foo:
... print(x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'classobj' object is not iterable
Even so, this works:
>>> for x in Foo.__iter__():
... print(x)
bar
baz
Add the __iter__ to the metaclass instead of the class itself (assuming Python 2.x):
class Foo(object):
bar = "bar"
baz = 1
class __metaclass__(type):
def __iter__(self):
for attr in dir(self):
if not attr.startswith("__"):
yield attr
For Python 3.x, use
class MetaFoo(type):
def __iter__(self):
for attr in dir(self):
if not attr.startswith("__"):
yield attr
class Foo(metaclass=MetaFoo):
bar = "bar"
baz = 1
this is how we make a class object iterable. provide the class with a iter and a next() method, then you can iterate over class attributes or their values.you can leave the next() method if you want to, or you can define next() and raise StopIteration on some condition.
e.g:
class Book(object):
def __init__(self,title,author):
self.title = title
self.author = author
def __iter__(self):
for each in self.__dict__.values():
yield each
>>> book = Book('The Mill on the Floss','George Eliot')
>>> for each in book: each
...
'George Eliot'
'The Mill on the Floss'
this class iterates over attribute value of class Book.
A class object can be made iterable by providing it with a getitem method too.
e.g:
class BenTen(object):
def __init__(self, bentenlist):
self.bentenlist = bentenlist
def __getitem__(self,index):
if index <5:
return self.bentenlist[index]
else:
raise IndexError('this is high enough')
>>> bt_obj = BenTen([x for x in range(15)])
>>>for each in bt_obj:each
...
0
1
2
3
4
now when the object of BenTen class is used in a for-in loop, getitem is called with succesively higher index value, till it raises IndexError.
You can iterate over the class's unhidden attributes with for attr in (elem for elem in dir(Foo) if elem[:2] != '__').
A less horrible way to spell that is:
def class_iter(Class):
return (elem for elem in dir(Class) if elem[:2] != '__')
then
for attr in class_iter(Foo):
pass
class MetaItetaror(type):
def __iter__(cls):
return iter(
filter(
lambda k: not k[0].startswith('__'),
cls.__dict__.iteritems()
)
)
class Klass:
__metaclass__ = MetaItetaror
iterable_attr_names = {'x', 'y', 'z'}
x = 5
y = 6
z = 7
for v in Klass:
print v
An instance of enum.Enum happens to be iterable, and while it is not a general solution, it is a reasonable option for some use cases:
from enum import Enum
class Foo(Enum):
bar = "qux"
baz = 123
>>> print(*Foo)
Foo.bar Foo.baz
names = [m.name for m in Foo]
>>> print(*names)
bar baz
values = [m.value for m in Foo]
print(*values)
>>> qux 123
As with .__dict__, the order of iteration using this Enum based approach is the same as the order of definition.
You can make class members iterable within just a single line.
Despite the easy and compact code there are two mayor features included, additionally:
Type checking allows using additional class members not to be iterated.
The technique is also working if (public) class methods are defined. The proposals above using the "__" string checking filtering method propably fail in such cases.
# How to make class members iterable in a single line within Python (O. Simon, 14.4.2022)
# Includes type checking to allow additional class members not to be iterated
class SampleVector():
def __init__(self, x, y, name):
self.x = x
self.y = y
self.name = name
def __iter__(self):
return [value for value in self.__dict__.values() if isinstance(value, int) or isinstance(value, float)].__iter__()
if __name__ == '__main__':
v = SampleVector(4, 5, "myVector")
print (f"The content of sample vector '{v.name}' is:\n")
for m in v:
print(m)
This solution is fairly close and inspired by answer 12 from Hans Ginzel and Vijay Shanker.

How can I make my classes usable as dict keys?

class A():
def __init__(self, data=''):
self.data = data
def __str__(self):
return str(self.data)
d = {}
elem = A()
d[elem] = 'abc'
elem2 = A()
print d[elem2] # KeyError
# actually elem2! was used not elem
how can I implement this without error?
I tried to get d[elem2] (not elem) with another instance of A() BUT with the same content.
The answer is yes, you need to redefine __hash__() and __eq__():
>>> class A(object):
... def __init__(self, data=''):
... self.data = data
... def __eq__(self, another):
... return hasattr(another, 'data') and self.data == another.data
... def __hash__(self):
... return hash(self.data)
...
>>> a1, a2, a3 = A('foo'), A('foo'), A('bar')
>>> d = {a1: 'foo'}
>>> d[a1]
'foo'
>>> d[a2]
'foo'
>>> d[a3]
Traceback (most recent call last):
File "", line 1, in
KeyError: __main__.A object at 0x927d0>
As explained in another comment default implementation of __hash__ is just simple identity, so if you want to make it more sophisticated, you need to define it explicitly.
What you did should work, as long as you don't override the __hash__() and __eq__() methods. It will use object identity as equality. If you want a different notion of equality, you can override the __hash__() and __eq__() methods of your class.

Categories

Resources