I have created a custom class, and I want to use the ** operator on a instance for passing it to a function. I have already defined __getitem__ and __iter__, but when I try f(**my_object), I'm getting
`TypeError: argument must be a mapping, not 'MyClass'`
What are the minimum required methods so that the custom class qualifies as a mapping?
** is not an operator, it is part of the call syntax:
If the syntax **expression appears in the function call, expression must evaluate to a mapping, the contents of which are treated as additional keyword arguments.
So if your class implements the Mapping methods, then you should be good to go. You'll need more than just __getitem__ and __iter__ here.
A Mapping is a Collection, so must define at least __getitem__, __iter__, and __len__; in addition most of __contains__, keys, items, values, get, __eq__, and __ne__ would be expected. If your custom class directly inherits from collections.abc.Mapping, you only need to implement the first three.
Demo:
>>> from collections.abc import Mapping
>>> class DemoMapping(Mapping):
... def __init__(self, a=None, b=None, c=None):
... self.a, self.b, self.c = a, b, c
... def __len__(self): return 3
... def __getitem__(self, name): return vars(self)[name]
... def __iter__(self): return iter('abc')
...
>>> def foo(a, b, c):
... print(a, b, c)
...
>>> foo(**DemoMapping(42, 'spam', 'eggs'))
42 spam eggs
If you run this under a debugger, you'll see that Python calls the .keys() method, which returns a dictionary view, which then delegates to the custom class __iter__ method when the view is iterated over. The values are then retrieved with a series of __getitem__ calls. So for your specific case, what was missing was the .keys() method.
In addition, note that Python may enforce that the keys are strings!
>>> class Numeric(Mapping):
... def __getitem__(self, name): return {1: 42, 7: 'spam', 11: 'eggs'}[name]
... def __len__(self): return 3
... def __iter__(self): return iter((1, 7, 11))
...
>>> dict(Numeric())
{1: 42, 7: 'spam', 11: 'eggs'}
>>> def foo(**kwargs): print(kwargs)
...
>>> foo(**Numeric())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
Emulating container types
The first set of methods is used [...] to emulate a mapping...
It is also recommended that mappings provide the methods keys(), values(), items(), get(), clear(), setdefault(), pop(), popitem(), copy(), and update() behaving similar to those for Python’s standard dictionary objects.
It is recommended that [...] mappings [...] implement the __contains__() method to allow efficient use of the in operator...
It is further recommended that [...] mappings [...] implement the __iter__() method to allow efficient iteration through the container
Related
In JavaScript, if I'm not sure whether every element of the chain exists/is not undefined, I can do foo?.bar, and if bar does not exist on foo, the interpreter will silently short circuit it and not throw an error.
Is there anything similar in Python? For now, I've been doing it like this:
if foo and foo.bar and foo.bar.baz:
# do something
My intuition tells me that this isn't the best way to check whether every element of the chain exists. Is there a more elegant/Pythonic way to do this?
If it's a dictionary you can use get(keyname, value)
{'foo': {'bar': 'baz'}}.get('foo', {}).get('bar')
Most pythonic way is:
try:
# do something
...
except (NameError, AttributeError) as e:
# do something else
...
You can use getattr:
getattr(getattr(foo, 'bar', None), 'baz', None)
You can use the Glom.
from glom import glom
target = {'a': {'b': {'c': 'd'}}}
glom(target, 'a.b.c', default=None) # returns 'd'
https://github.com/mahmoud/glom
I like modern languages like Kotlin which allow this:
foo?.bar?.baz
Recently I had fun trying to implement something similar in python:
https://gist.github.com/karbachinsky/cc5164b77b09170edce7e67e57f1636c
Unfortunately, the question mark is not a valid symbol in attribute names in python, thus I used a similar mark from Unicode :)
Combining a few things I see here.
from functools import reduce
def optional_chain(obj, keys):
try:
return reduce(getattr, keys.split('.'), obj)
except AttributeError:
return None
optional_chain(foo, 'bar.baz')
Or instead extend getattr so you can also use it as a drop-in replacement for getattr
from functools import reduce
def rgetattr(obj, attr, *args):
def _getattr(obj, attr):
return getattr(obj, attr, *args)
return reduce(_getattr, attr.split('.'), obj)
With rgetattr it can still raise an AttributeError if the path does not exist, and you can specify your own default instead of None.
Combining some of the other answers into a function gives us something that's easily readable and something that can be used with objects and dictionaries.
def optional_chain(root, *keys):
result = root
for k in keys:
if isinstance(result, dict):
result = result.get(k, None)
else:
result = getattr(result, k, None)
if result is None:
break
return result
Using this function you'd just add the keys/attributes after the first argument.
obj = {'a': {'b': {'c': {'d': 1}}}}
print(optional_chain(obj, 'a', 'b'), optional_chain(obj, 'a', 'z'))
Gives us:
{'c': {'d': 1}} None
Classes can override __getattr__ to return a default value for missing attributes:
class Example:
def __getattr__(self, attr): # only called when missing
return None
Testing it:
>>> ex = Example()
>>> ex.attr = 1
>>> ex.attr
1
>>> ex.missing # evaluates to `None
>>>
However, this will not allow for chaining:
>>> ex.missing.missing
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'missing'
Nor will it deal with attempts to call methods that are absent:
>>> ex.impossible()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
To fix this, we can make a proxy object:
class GetAnything:
def __getattr__(self, attr):
return self
def __call__(self, *args, **kwargs): # also allow calls to work
return self
def __repr__(self):
return '<Missing value>'
# Reassign the name to avoid making more instances
GetAnything = GetAnything()
And return that instead of None:
class Example:
def __getattr__(self, attr): # only called when missing
return GetAnything
Now it chains as desired:
>>> Example().missing_attribute.missing_method().whatever
<Missing value>
Here's some syntactic sugar to make chaining with getattr look more like the fluent interfaces of other languages. It's definitely not "Pythonic", but it allows for something simpler to write.
The idea is to abuse the # operator added in Python 3.5 (to support matrix multiplication in Numpy). We define a class r such that its instances, when matrix-multiplied on the right of another object, invoke getattr. (The combination #r, of course, is read "attr".)
class r:
def __init__(self, name, value=None):
self._name = name
self._value = value
def __rmatmul__(self, obj):
return getattr(obj, self._name, self._value)
Now we can chain attribute accesses easily, without having to modify any other classes (and of course it works on built-in types):
>>> 'foo'#r('bar')#r('baz') # None
>>>
However, the order of operations is inconvenient with method calls:
>>> 'foo bar'#r('split')()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'r' object is not callable
>>> ('foo bar'#r('split'))()
['foo', 'bar']
Python 3.10 introduced the match statement in PEP-634, with the tutorial in PEP-636 being a nice reference.
This statement allow these sorts of "chained" operations to be performed, but note that they are statements and not expressions.
For example, OP could instead do:
match foo:
case object(bar=object(baz=baz)) if baz:
# do something with baz
The reason for needing object is that everything is a subtype of it and hence it always succeeds. It then goes on to check that the attribute exists, which might fail. Exceptions wouldn't be thrown if the attribute didn't exist, just the case wouldn't match and it would move onto the next one (which in this case doesn't exist, so nothing would be done).
A more realistic example would check something more specific, e.g.:
from collections import namedtuple
Foo = namedtuple('Foo', ['bar'])
Bar = namedtuple('Bar', ['baz'])
def fn(x):
match x:
case Foo(bar=Bar(baz=baz)):
return baz
print(fn(Foo(bar=Bar(baz='the value'))))
print(fn(None))
print(fn(1))
which would output:
the value
None
None
If instead you wanted to destructure into dictionaries, you might use something like:
foo = {'bar': {'baz': 'the value'}}
match foo:
case {'bar': {'baz': baz}}:
print(baz)
My question:
It seems that __getattr__ is not called for indexing operations, ie I can't use __getattr__ on a class A to provide A[...]. Is there a reason for this? Or a way to get around it so that __getattr__ can provide that functionality without having to explicitly define __getitem__, __setitem__, etc on A?
Minimal Example:
Let's say I define two nearly identical classes, Explicit and Implicit. Each creates a little list self._arr on initiation, and each defines a __getattr__ that just passes all attribute requests to self._arr. The only difference is that Explicit also defines __getitem__ (by just passing it on to self._arr).
# Passes all attribute requests on to a list it contains
class Explicit():
def __init__(self):
self._arr=[1,2,3,4]
def __getattr__(self,attr):
print('called __getattr_')
return getattr(self._arr,attr)
def __getitem__(self,item):
return self._arr[item]
# Same as above but __getitem__ not defined
class Implicit():
def __init__(self):
self._arr=[1,2,3,4]
def __getattr__(self,attr):
print('called __getattr_')
return getattr(self._arr,attr)
This works as expected:
>>> e=Explicit()
>>> print(e.copy())
called __getattr_
[1, 2, 3, 4]
>>> print(hasattr(e,'__getitem__'))
True
>>> print(e[0])
1
But this doesn't:
>>> i=Implicit()
>>> print(i.copy())
called __getattr_
[1, 2, 3, 4]
>>> print(hasattr(i,'__getitem__'))
called __getattr_
True
>>> print(i.__getitem__(0))
called __getattr_
1
>>> print(i[0])
TypeError: 'Implicit' object does not support indexing
Python bypasses __getattr__, __getattribute__, and the instance dict when looking up "special" methods for implementing language mechanics. (For the most part, special methods are ones with two underscores on each side of the name.) If you were expecting i[0] to invoke i.__getitem__(0), which would in turn invoke i.__getattr__('__getitem__')(0), that's why that didn't happen.
Why does a class need to define __iter__() returning self, to get an iterator of the class?
class MyClass:
def __init__(self):
self.state = 0
def __next__(self):
self.state += 1
if self.state > 4:
raise StopIteration
return self.state
myObj = MyClass()
for i in myObj:
print(i)
Console log:
Traceback (most recent call last):
for i in myObj:
TypeError: 'MyClass' object is not iterable
the answer https://stackoverflow.com/a/9884259/4515198, says
An iterator is an object with a next (Python 2) or __next__ (Python 3) method.
The task of adding the following:
def __iter__(self):
return self
is to return an iterator, or an object of the class, which defines the __next__() method.
But, isn't the task of returning an object of MyClass (which defines the __next__() method) already done by the __new__() method, when MyClass is instantiated in the line myObj = MyClass() ?
Won't the objects of a class defining __next__() method, be iterators by themselves?
I have studied the questions What is the use of returning self in the __iter__ method? and Build a Basic Python Iterator, but I am still unable to understand the reason for having an __iter__() method returning self.
The answer to the question of why the __iter__() method is necessary is that for for-loops always start by calling iter() on an object to get an iterator. That is why even iterators themselved need an __iter__() method to work with for-loops. After for calls iter(), then it calls __next__() on the resulting iterator to obtain a value.
The rules for creating iterables and iterators are:
1) Iterables have an __iter__() method that returns an iterator.
2) Iterators have a __next__() method that returns a value, that updates the state, and that raises StopIteration when complete.
3) Iterators themselves have a __iter__() method that returns self. That means that all iterators are self-iterable.
The benefit of the last rule for iterators having an __iter__() method that returns self is that it allows us to pass around partially consumed iterators:
>>> s = 'hello world'
>>> it = iter(s)
>>> next(it)
'h'
>>> next(it)
'e'
>>> list(it) # Doesn't start from the beginning
['l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
Here's another example that depends on iterators being self-iterable without restarting:
>>> s = 'hello world'
>>> it = iter(s)
>>> list(zip(it, it))
[('h', 'e'), ('l', 'l'), ('o', ' '), ('w', 'o'), ('r', 'l')]
Notes:
1) An alternative way to make an iterable is to supply a __getitem__() method that accepts consecutive indices and raises IndexError when complete. This is how str objects iterated in Python 2.
2) Some objects like files are their own iterator. That means that you can call next() directly on a file object. It also means that files cannot have multiple, independent iterators (the file object itself has the state tracking the position within the file).
3) The iterator design pattern described above isn't Python specific. It is a general purpose design pattern for many OOP languages: https://en.wikipedia.org/wiki/Iterator_pattern
isn't that already done by the __new__() method
Nope, __new__ is just another method, it doesn't automatically create __iter__ for an object.
Won't the objects of a class defining __next__() method, be iterators by themselves?
Not necessarily, as your first class defining __next__ but not __iter__ shows. __next__ is needed if you need to support iteration since it produces the values. __iter__ is needed since that's what's called on an object in the for statement in order for it to get an iterator.
You could have a class that only defines __next__ and somewhat works (it is limited) but, it needs to be returned from someones __iter__ . For example, the class MyClass returns a class CustomIter that only defines __next__:
class MyClass:
def __iter__(self):
return CustomIter()
class CustomIter(object):
def __init__(self):
self.state = 0
def __next__(self):
self.state += 1
if self.state > 4:
raise StopIteration
return self.state
You'll need an __iter__ defined on an object that will return another object (could be itself) on which __next__ is defined.
If your class defines __iter__ as:
def __iter__(self): return self
then you need to define __next__ on type(self) (the class) since you return the instance itself. __next__ is going to get called on self until no more values can be produced.
The other case is when __iter__ simply returns another object which defines __next__ (as per my first example). You can alternatively do that by making __iter__ a generator.
For example:
class MyClass:
def __init__(self):
self.state = 0
def __iter__(self):
for i in range(10): yield i
doesn't define a __next__. When iter is called on it though:
g = iter(MyClass())
it returns a generator g that defines __next__:
g = iter(MyClass())
g.__next__() # 0
g.__next__() # 1
Let's say I've got a simple class in python
class Wharrgarbl(object):
def __init__(self, a, b, c, sum, version='old'):
self.a = a
self.b = b
self.c = c
self.sum = 6
self.version = version
def __int__(self):
return self.sum + 9000
def __what_goes_here__(self):
return {'a': self.a, 'b': self.b, 'c': self.c}
I can cast it to an integer very easily
>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> int(w)
9006
Which is great! But, now I want to cast it to a dict in a similar fashion
>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> dict(w)
{'a': 'one', 'c': 'three', 'b': 'two'}
What do I need to define for this to work? I tried substituting both __dict__ and dict for __what_goes_here__, but dict(w) resulted in a TypeError: Wharrgarbl object is not iterable in both cases. I don't think simply making the class iterable will solve the problem. I also attempted many googles with as many different wordings of "python cast object to dict" as I could think of but couldn't find anything relevant :{
Also! Notice how calling w.__dict__ won't do what I want because it's going to contain w.version and w.sum. I want to customize the cast to dict in the same way that I can customize the cast to int by using def int(self).
I know that I could just do something like this
>>> w.__what_goes_here__()
{'a': 'one', 'c': 'three', 'b': 'two'}
But I am assuming there is a pythonic way to make dict(w) work since it is the same type of thing as int(w) or str(w). If there isn't a more pythonic way, that's fine too, just figured I'd ask. Oh! I guess since it matters, this is for python 2.7, but super bonus points for a 2.4 old and busted solution as well.
There is another question Overloading __dict__() on python class that is similar to this one but may be different enough to warrant this not being a duplicate. I believe that OP is asking how to cast all the data in his class objects as dictionaries. I'm looking for a more customized approach in that I don't want everything in __dict__ included in the dictionary returned by dict(). Something like public vs private variables may suffice to explain what I'm looking for. The objects will be storing some values used in calculations and such that I don't need/want to show up in the resulting dictionaries.
UPDATE:
I've chosen to go with the asdict route suggested but it was a tough choice selecting what I wanted to be the answer to the question. Both #RickTeachey and #jpmc26 provided the answer I'm going to roll with but the former had more info and options and landed on the same result as well and was upvoted more so I went with it. Upvotes all around though and thanks for the help. I've lurked long and hard on stackoverflow and I'm trying to get my toes in the water more.
There are at least five six ways. The preferred way depends on what your use case is.
Option 1:
Simply add an asdict() method.
Based on the problem description I would very much consider the asdict way of doing things suggested by other answers. This is because it does not appear that your object is really much of a collection:
class Wharrgarbl(object):
...
def asdict(self):
return {'a': self.a, 'b': self.b, 'c': self.c}
Using the other options below could be confusing for others unless it is very obvious exactly which object members would and would not be iterated or specified as key-value pairs.
Option 1a:
Inherit your class from 'typing.NamedTuple' (or the mostly equivalent 'collections.namedtuple'), and use the _asdict method provided for you.
from typing import NamedTuple
class Wharrgarbl(NamedTuple):
a: str
b: str
c: str
sum: int = 6
version: str = 'old'
Using a named tuple is a very convenient way to add lots of functionality to your class with a minimum of effort, including an _asdict method. However, a limitation is that, as shown above, the NT will include all the members in its _asdict.
If there are members you don't want to include in your dictionary, you'll need to specify which members you want the named tuple _asdict result to include. To do this, you could either inherit from a base namedtuple class using the older collections.namedtuple API:
from collections import namedtuple as nt
class Wharrgarbl(nt("Basegarble", "a b c")):
# note that the typing info below isn't needed for the old API
a: str
b: str
c: str
sum: int = 6
version: str = 'old'
...or you could create a base class using the newer API, and inherit from that, using only the dictionary members in the base class:
from typing import NamedTuple
class Basegarbl(NamedTuple):
a: str
b: str
c: str
class Wharrgarbl(Basegarbl):
sum: int = 6
version: str = 'old'
Another limitation is that NT is read-only. This may or may not be desirable.
Option 2:
Implement __iter__.
Like this, for example:
def __iter__(self):
yield 'a', self.a
yield 'b', self.b
yield 'c', self.c
Now you can just do:
dict(my_object)
This works because the dict() constructor accepts an iterable of (key, value) pairs to construct a dictionary. Before doing this, ask yourself the question whether iterating the object as a series of key,value pairs in this manner- while convenient for creating a dict- might actually be surprising behavior in other contexts. E.g., ask yourself the question "what should the behavior of list(my_object) be...?"
Additionally, note that accessing values directly using the get item obj["a"] syntax will not work, and keyword argument unpacking won't work. For those, you'd need to implement the mapping protocol.
Option 3:
Implement the mapping protocol. This allows access-by-key behavior, casting to a dict without using __iter__, and also provides two types of unpacking behavior:
mapping unpacking behavior: {**my_obj}
keyword unpacking behavior, but only if all the keys are strings: dict(**my_obj)
The mapping protocol requires that you provide (at minimum) two methods together: keys() and __getitem__.
class MyKwargUnpackable:
def keys(self):
return list("abc")
def __getitem__(self, key):
return dict(zip("abc", "one two three".split()))[key]
Now you can do things like:
>>> m=MyKwargUnpackable()
>>> m["a"]
'one'
>>> dict(m) # cast to dict directly
{'a': 'one', 'b': 'two', 'c': 'three'}
>>> dict(**m) # unpack as kwargs
{'a': 'one', 'b': 'two', 'c': 'three'}
As mentioned above, if you are using a new enough version of python you can also unpack your mapping-protocol object into a dictionary comprehension like so (and in this case it is not required that your keys be strings):
>>> {**m}
{'a': 'one', 'b': 'two', 'c': 'three'}
Note that the mapping protocol takes precedence over the __iter__ method when casting an object to a dict directly (without using kwarg unpacking, i.e. dict(m)). So it is possible- and might be sometimes convenient- to cause the object to have different behavior when used as an iterable (e.g., list(m)) vs. when cast to a dict (dict(m)).
But note also that with regular dictionaries, if you cast to a list, it will give the KEYS back, and not the VALUES as you require. If you implement another nonstandard behavior for __iter__ (returning values instead of keys), it could be surprising for other people using your code unless it is very obvious why this would happen.
EMPHASIZED: Just because you CAN use the mapping protocol, does NOT mean that you SHOULD do so. Does it actually make sense for your object to be passed around as a set of key-value pairs, or as keyword arguments and values? Does accessing it by key- just like a dictionary- really make sense? Would you also expect your object to have other standard mapping methods such as items, values, get? Do you want to support the in keyword and equality checks (==)?
If the answer to these questions is yes, it's probably a good idea to not stop here, and consider the next option instead.
Option 4:
Look into using the 'collections.abc' module.
Inheriting your class from 'collections.abc.Mapping or 'collections.abc.MutableMapping signals to other users that, for all intents and purposes, your class is a mapping * and can be expected to behave that way. It also provides the methods items, values, get and supports the in keyword and equality checks (==) "for free".
You can still cast your object to a dict just as you require, but there would probably be little reason to do so. Because of duck typing, bothering to cast your mapping object to a dict would just be an additional unnecessary step the majority of the time.
This answer from me about how to use ABCs might also be helpful.
As noted in the comments below: it's worth mentioning that doing this the abc way essentially turns your object class into a dict-like class (assuming you use MutableMapping and not the read-only Mapping base class). Everything you would be able to do with dict, you could do with your own class object. This may be, or may not be, desirable.
Also consider looking at the numerical abcs in the numbers module:
https://docs.python.org/3/library/numbers.html
Since you're also casting your object to an int, it might make more sense to essentially turn your class into a full fledged int so that casting isn't necessary.
Option 5:
Look into using the dataclasses module (Python 3.7+ only), which includes a convenient asdict() utility method.
from dataclasses import dataclass, asdict, field, InitVar
#dataclass
class Wharrgarbl(object):
a: int
b: int
c: int
sum: InitVar[int] # note: InitVar will exclude this from the dict
version: InitVar[str] = "old"
def __post_init__(self, sum, version):
self.sum = 6 # this looks like an OP mistake?
self.version = str(version)
Now you can do this:
>>> asdict(Wharrgarbl(1,2,3,4,"X"))
{'a': 1, 'b': 2, 'c': 3}
Option 6:
Use typing.TypedDict, which has been added in python 3.8.
NOTE: option 6 is likely NOT what the OP, or other readers based on the title of this question, are looking for. See additional comments below.
class Wharrgarbl(TypedDict):
a: str
b: str
c: str
Using this option, the resulting object is a dict (emphasis: it is not a Wharrgarbl). There is no reason at all to "cast" it to a dict (unless you are making a copy).
And since the object is a dict, the initialization signature is identical to that of dict and as such it only accepts keyword arguments or another dictionary.
>>> w = Wharrgarbl(a=1,b=2,b=3)
>>> w
{'a': 1, 'b': 2, 'c': 3}
>>> type(w)
<class 'dict'>
Emphasized: the above "class" Wharrgarbl isn't actually a new class at all. It is simply syntactic sugar for creating typed dict objects with specific keys ONLY and value fields of different types for the type checker. At run time, it is still nothing more than a dict.
As such this option can be pretty convenient for signaling to readers of your code (and also to a type checker such as mypy) that such a dict object is expected to have specific keys with specific value types.
But this means you cannot, for example, add other methods, although you can try:
class MyDict(TypedDict):
def my_fancy_method(self):
return "world changing result"
...but it won't work:
>>> MyDict().my_fancy_method()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'my_fancy_method'
* "Mapping" has become the standard "name" of the dict-like duck type
There is no magic method that will do what you want. The answer is simply name it appropriately. asdict is a reasonable choice for a plain conversion to dict, inspired primarily by namedtuple. However, your method will obviously contain special logic that might not be immediately obvious from that name; you are returning only a subset of the class' state. If you can come up with with a slightly more verbose name that communicates the concepts clearly, all the better.
Other answers suggest using __iter__, but unless your object is truly iterable (represents a series of elements), this really makes little sense and constitutes an awkward abuse of the method. The fact that you want to filter out some of the class' state makes this approach even more dubious.
something like this would probably work
class MyClass:
def __init__(self,x,y,z):
self.x = x
self.y = y
self.z = z
def __iter__(self): #overridding this to return tuples of (key,value)
return iter([('x',self.x),('y',self.y),('z',self.z)])
dict(MyClass(5,6,7)) # because dict knows how to deal with tuples of (key,value)
I think this will work for you.
class A(object):
def __init__(self, a, b, c, sum, version='old'):
self.a = a
self.b = b
self.c = c
self.sum = 6
self.version = version
def __int__(self):
return self.sum + 9000
def __iter__(self):
return self.__dict__.iteritems()
a = A(1,2,3,4,5)
print dict(a)
Output
{'a': 1, 'c': 3, 'b': 2, 'sum': 6, 'version': 5}
Like many others, I would suggest implementing a to_dict() function rather than (or in addition to) allowing casting to a dictionary. I think it makes it more obvious that the class supports that kind of functionality. You could easily implement such a method like this:
def to_dict(self):
class_vars = vars(MyClass) # get any "default" attrs defined at the class level
inst_vars = vars(self) # get any attrs defined on the instance (self)
all_vars = dict(class_vars)
all_vars.update(inst_vars)
# filter out private attributes
public_vars = {k: v for k, v in all_vars.items() if not k.startswith('_')}
return public_vars
It's hard to say without knowing the whole context of the problem, but I would not override __iter__.
I would implement __what_goes_here__ on the class.
as_dict(self:
d = {...whatever you need...}
return d
I am trying to write a class that is "both" a list or a dict. I want the programmer to be able to both "cast" this object to a list (dropping the keys) or dict (with the keys).
Looking at the way Python currently does the dict() cast: It calls Mapping.update() with the object that is passed. This is the code from the Python repo:
def update(self, other=(), /, **kwds):
''' D.update([E, ]**F) -> None. Update D from mapping/iterable E and F.
If E present and has a .keys() method, does: for k in E: D[k] = E[k]
If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
In either case, this is followed by: for k, v in F.items(): D[k] = v
'''
if isinstance(other, Mapping):
for key in other:
self[key] = other[key]
elif hasattr(other, "keys"):
for key in other.keys():
self[key] = other[key]
else:
for key, value in other:
self[key] = value
for key, value in kwds.items():
self[key] = value
The last subcase of the if statement, where it is iterating over other is the one most people have in mind. However, as you can see, it is also possible to have a keys() property. That, combined with a __getitem__() should make it easy to have a subclass be properly casted to a dictionary:
class Wharrgarbl(object):
def __init__(self, a, b, c, sum, version='old'):
self.a = a
self.b = b
self.c = c
self.sum = 6
self.version = version
def __int__(self):
return self.sum + 9000
def __keys__(self):
return ["a", "b", "c"]
def __getitem__(self, key):
# have obj["a"] -> obj.a
return self.__getattribute__(key)
Then this will work:
>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> dict(w)
{'a': 'one', 'c': 'three', 'b': 'two'}
Here is very clean and fast solution
I created a function that converts any custom class to dict
def convert_to_dict(args: dict):
json = dict()
for key, value in args.items():
key_vals = str(key).split("_")
last_index = len(key_vals)
json[str(key_vals[last_index-1])] = value
return json
what you need is to supply it object.__dict__ then it will do the job for you to clean it and help you store it in databse.
Going off of the asdict solution, here's a useful mixin for if you want to add asdict to several different classes.
Adapted from: https://www.pythontutorial.net/python-oop/python-mixin/
class DictMixin:
def asdict(self):
return self._traverse_dict(self._attrs)
def _traverse_dict(self, attributes: dict) -> dict:
result = {}
for key, value in attributes.items():
result[key] = self._traverse(value)
return result
def _traverse(self, value):
if isinstance(value, DictMixin):
return value.asdict()
elif isinstance(value, dict):
return self._traverse_dict(value)
elif isinstance(value, list):
return [self._traverse(v) for v in value]
else:
return value
Which you can then use:
class FooBar(DictMixin):
_attrs = ["foo", "hello"]
def __init__(self):
self.foo = "bar"
self.hello = "world"
>>> a = FooBar()
>>> a.asdict()
{
"foo": "bar",
"hello": "world"
}
You can create a folder like 'Strategy' then you can use pickle to save and load the objects of your class.
import pickle
import os
# Load object as dictionary ---------------------------------------------------
def load_object():
file_path = 'Strategy\\All_Pickles.hd5'
if not os.path.isfile(file_path):
return {}
with open(file_path, 'rb') as file:
unpickler = pickle.Unpickler(file)
return dict(unpickler.load())
# Save object as dictionary ---------------------------------------------------
def save_object(name, value):
file_path = 'Strategy\\All_Pickles.hd5'
object_dict = load_object()
with open(file_path, 'wb') as file:
object_dict[name] = value
pickle.dump(object_dict, file)
return True
class MyClass:
def __init__(self, name):
self.name = name
def show(self):
print(self.name)
save_object('1', MyClass('Test1'))
save_object('2', MyClass('Test2'))
objects = load_object()
obj1 = objects['1']
obj2 = objects['2']
obj1.show()
obj2.show()
I created two objects of one class and called a method of the class. I hope, it can help you.
I want to be able to do something like the following
class C(object):
# I store a series of values in some way
# what do I need to implement to act like an array of arguments
c=C()
result=f(*c)
What does the *"operator" invoke on the instance in this usage?
There are two ways to control the behavior of the * operator when it is used like that:
Overload the __iter__ special method:
>>> class C(object):
... def __init__(self, lst):
... self.lst = lst
... def __iter__(self):
... return iter(self.lst)
...
>>> def f(a, b, c):
... print "Arguments: ", a, b, c
...
>>> c = C([1, 2, 3])
>>> f(*c)
Arguments: 1 2 3
>>>
Overload the __getitem__ special method:
>>> class C(object):
... def __init__(self, lst):
... self.lst = lst
... def __getitem__(self, key):
... return self.lst[key]
...
>>> def f(a, b, c):
... print "Arguments: ", a, b, c
...
>>> c = C([1, 2, 3])
>>> f(*c)
Arguments: 1 2 3
>>>
One way you can do it is to subclass tuple or list.
People call this "positional expansion" or argument unpacking. Your instance should provide an __iter__ method, which is called when iterating over this object. However, I think the cleanest approach would be to subclass collections.Iterable, which is the abstract base class for all iterables in Python.
Note that this is the same question for keyword argument unpacking, requiring the object to be a mapping.
Edit: I am still trying to find the exact implementation in this case to see which C API call is used for unpacking. This would yield the precise answer to this question. Any pointers?