pcattori / maps Goto Github PK
View Code? Open in Web Editor NEWMaps: Python's missing mappings
Home Page: http://maps.readthedocs.io
License: MIT License
Maps: Python's missing mappings
Home Page: http://maps.readthedocs.io
License: MIT License
import maps
RGB = maps.namedfrozen('RGB', [...])
CMYK = maps.namedfixedkey('CMYK', [...])
currently fields
must be a list
just cast to list
(or maybe tuple
?) within metaclass
Mapping from standart collections
has empty __slots__ = ()
.
I think it'll be fine to add slots for FrozenMap
and others.
For FrozenMap
:
__slots__ = [
'_data',
'_hash'
]
The way namedfixedkeymap
and namedfrozenmap
are created in the current version of code, there are a couple additional field names that aren't supported beyond what are already accounted for in the _validate_name
method.
The template code is:
def __init__(self, {args}):
super(type(self), self).__init__()
self._data = collections.OrderedDict({kwargs})
Because each of the field names gets inserted into {args}
, the following field names are forbidden but not currently caught:
type
self
collections
Here's code demonstrating each of the errors:
>>> Foo = maps.namedfrozen('Foo', ['type'])
>>> foo = Foo('abc')
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-58-eb714c1f9e6f> in <module>()
----> 1 foo = Foo('abc')
<string> in __init__(self, type)
TypeError: 'str' object is not callable
>>> Bar = maps.namedfrozen('Bar', ['self'])
Traceback (most recent call last):
File "/Users/jamesallen/.pyenv/versions/3.6.1/envs/cf2-local/lib/python3.6/site-packages/IPython/core/interactiveshell.py", line 2862, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-59-53de955fd15f>", line 1, in <module>
Bar = maps.namedfrozen('Bar', ['self'])
File "/Users/jamesallen/.pyenv/versions/3.6.1/envs/cf2-local/lib/python3.6/site-packages/maps/__init__.py", line 29, in namedfrozen
return NamedFrozenMapMeta(typename, fields)
File "/Users/jamesallen/.pyenv/versions/3.6.1/envs/cf2-local/lib/python3.6/site-packages/maps/namedfrozenmap.py", line 104, in __new__
exec(template.format(args=args, kwargs=kwargs), namespace)
File "<string>", line 1
SyntaxError: duplicate argument 'self' in function definition
>>> Baz = maps.namedfrozen('Baz', ['collections'])
>>> baz = Baz('abc')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-61-10871cae25dc> in <module>()
----> 1 baz = Baz('abc')
<string> in __init__(self, collections)
AttributeError: 'str' object has no attribute 'OrderedDict'
The simplest fix is to disallow these field names, but it also shouldn't be much more work to come up with unique identifiers for self
, type
, and collections
. I'll look into it and submit a PR shortly
maybe like this:
import maps
# namedfrozen(typename, fields, defaults)
RGB = maps.namedfrozen('RGB', ['red', 'green', 'blue', 'alpha'], defaults=dict(alpha=0))
...
RGB = maps.namedfrozen('RGB', ['red', 'green', 'blue', 'alpha'], defaults={'alpha': 0})
but this could break the rule of "positional args first, then key word args" eg. if you define defaults with gaps between them
import maps
RGB = maps.namedfrozen('RGB', ['red', 'green', 'blue'], dict(alpha=0))
but need to be careful about ordering of kwargs
^ actually, this is again similar to function def notation, where order of kwargs is not that important. though the issue is that the user may not know in what order those kwargs got defined. In Python 3, we can avoid this via the bare *
are keyword-only params. But in python 2, this is trickier
# in python3.6+ kwargs order is preserved
import collections
import maps
RGB = maps.namedfrozen('RGB', ['red', 'green', 'blue'], defaults=collections.OrderedDict(alpha=0))
# or without depending on collections.OrderedDict
RGB = maps.namedfrozen('RGB', ['red', 'green', 'blue'], defaults=[('alpha', 0)])
^ this is similar to function definition notation where positional args come before keyword args.
import maps
# namedfrozen(typename, fields) where fields can specify defaults
RGB = maps.namedfrozen('RGB', ['red', 'green', 'blue', ('alpha', 0)])
import maps
# namedfrozen(typename, fields) where fields can specify defaults
RGB = maps.namedfrozen('RGB', ['red', 'green', 'blue', 'alpha'])
def rgb(red, green, blue, alpha=0):
return RGB(red, green, blue, alpha)
it's way easier for package maintainers in distros to take archive from github than from PyPI
bonus: this makes it easier to work with sphinx.ext.autodoc
to document metaclass behavior
To reproduce:
m = maps.NamedDict()
hasattr(m, 'x')
I found this error while trying to serialize an object containing named maps with jsonpickle.
NamedMap
to (NamedFrozenMap
or FrozenNamedMap
)?
That would also help make FixedKeyNamedMap
make more sense as a name
things to mention:
namedtuple
until you realize you want to access by namenamedtuple
until you want to **
unpacknamedfrozen
with namedfixedkey
if you realize you want to edit values later on (no need to handle _replace
)it's needed for proper packaging in distros
ala namedtuple
Attempting to subclass NamedDict
leads to an infinite recursion error when attempting to print out the repr
of an instance of the class. This is due to the use of type(self)
in the super
call.
The docs address this (http://maps.readthedocs.io/en/latest/user/design.html#class-vs-function), but I think there is a use case for being able to subclass NamedDict
- specifically, if you want to be able to distinguish between types of NamedDict using isinstance
, and/or if you want to add specific functionality to a bag of data.
NamedDict is a trivial fix - just change the occurrences of super(type(self), self)
to super(NamedDict, self)
.
However, the metaclasses NamedFrozenMapMeta and NamedFixedKeyMapMeta still contain occurrences of super(type(self), self)
. Due to the way the subclasses are created (using exec
), it's not immediately apparent to me how one would address this issue - there seems to be a chicken and egg problem in we need to pass a reference to the class into the namespace, but we don't have that reference until it's created. I tried attaching __init__
to the class after creation via type.__new__
, but that led to other issues:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 2, in __init__
File "/Users/jamesallen/src/github.com/jamesmallen/maps/maps/frozenmap.py", line 28, in __init__
self._data = dict(*args, **kwargs)
File "/Users/jamesallen/src/github.com/jamesmallen/maps/maps/namedfrozenmap.py", line 66, in _setattr
super(NamedFrozenMapMeta, self).__setattr__(name, value)
TypeError: super(type, obj): obj must be an instance or subtype of type
eg:
def set_stage(self, stage, number):
if number < -6 or number > 6:
raise ValueError('Nothing happened!')
self[stage] = number
StatStages = maps.namedmap('StatStages', [...], methods={'set_stage', set_stage}
I was wondering if you can export a recursive version that can handle recurse into dicts and lists.
Here is an example structure I am working with:
I envision this to be similar to the fromJS
function from immutableJS: http://facebook.github.io/immutable-js/docs/#/fromJS
EDIT: I meant to use FrozenMap
in the screenshot above.
$ python
Python 3.7.3 (default, Apr 3 2019, 19:16:38)
[GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import maps
>>> from collections import defaultdict
>>> maps.FrozenMap.recurse({'a': {'b': 'c'}})
FrozenMap({'a': FrozenMap({'b': 'c'})})
>>> d = defaultdict(dict)
>>> d['a']['b'] = 'c'
>>> d
defaultdict(<class 'dict'>, {'a': {'b': 'c'}})
>>> maps.FrozenMap.recurse(d)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/crushingismybusiness/.local/share/virtualenvs/sample_service-cH3G9tDP/lib/python3.7/site-packages/maps/frozenmap.py", line 44, in recurse
return utils._recurse(obj, map_fn=cls, list_fn=list_fn, object_fn=object_fn)
File "/home/crushingismybusiness/.local/share/virtualenvs/sample_service-cH3G9tDP/lib/python3.7/site-packages/maps/utils.py", line 52, in _recurse
return map_fn(**cls((k, _recurse(v, **kwargs)) for k,v in obj.items()))
TypeError: first argument must be callable or None
explain why the mascot is a raccoon
NamedDict
FrozenMap
FixedKeyMap
Hi,
Would it be possible to add a binary operation between FrozenMap
instances that merges them?
For example, it would be nice for the following code to work:
import maps
x = maps.FrozenMap({'a': 2, 'b': 4})
y = maps.FrozenMap({'b': 5, 'c': 6})
assert x.update(y) == maps.FrozenMap({'a': 2, 'b': 5, 'c': 6})
assert x['b'] == 4
assert 'c' not in x
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.