Back in early 2016 there was an idea submitted[1] by Joseph Martinot-Lagarde in regard to anonymous namedtuples. The crux of the submission was the idea of instantiating a namedtuple using only Python's generic tuple syntax plus keyword arguments. At the time, the major rebuttal against this idea however was that, since kwargs are an unordered collection, it would be non-trivial to figure out a proper convention for mapping an unordered collection onto an ordered one. Times have changed however, and with python3.6/3.7 we now have the guarantee that dictionaries are insertion ordered. This means that by extension keyword arguments are also ordered, and thus the major point of contest against anonymous namedtuples is now moot. And as such I would like to once again suggest anonymous namedtuples as a potential PEP. Proposal ======= The proposal ultimately is the same as before: ``` my_point = (x=12, y=16) # (x=12, y=16) my_point[0] # 12 ``` where kwargs can be passed to a tuple's instantiation [either via tuple() or just ()], and these tuples would have all the same properties of a namedtuple. A basic, pure-python implementation is actually quite trivial now given that kwargs are ordered. Observe: ``` def atuple(**kwargs): signature = " ".join(kwargs) _type = atuple.__dict__.get(signature) if not _type: _type = namedtuple(atuple.__name__, signature) atuple.__dict__[signature] = _type return _type(**kwargs) ``` This has none of the problems suggested in the previous rebuttal. anonymous tuples of incorrect ordering raise the assertion error, as expected: ```
assert atuple(x=12, y=16) == atuple(y=16, x=12) Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError
...and by extension it preserves the users's specified ordering as given by **kwargs.
pt = atuple(r=3, theta=0.5, phi=0.25) pt atuple(r=3, theta=0.5, phi=0.25)
Given that this implementation caches the type by the keyword signature, anonymous tuples of the same signature are recognized as the same type. However, given differing values, they are not equal.
type(atuple(x=1,y=2)) == type(atuple(x=1,y=2)) True atuple(x=1,y=2) == atuple(x=1,y=3) False
This allows us to provide positional arguments to the construct, just like regular namedtuples, and passing a mismatch throws an error as expected.
pt = atuple(x=12, y=16) pt atuple(x=12, y=16) type(pt)(1,2) atuple(x=1, y=2) type(pt)(1,2,3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: atuple.__new__() takes 3 positional arguments but 4 were given
As we can see, the introduction of insertion-ordered dictionaries solves all of the issues previously raised with regard to anonymous namedtuples.
References:
[1] https://mail.python.org/pipermail/python-ideas/2016-April/039857.html
The proposal doesn't work well with type hints: atuple(a=1, b=2) and atuple(a="a", b="b") generates the same type. Also, the automatic creation of a new type on-demand can lead to uncontrollable memory growth. On Thu, Mar 24, 2022 at 6:15 PM <michael@michaelgreen.dev> wrote:
Back in early 2016 there was an idea submitted[1] by Joseph Martinot-Lagarde in regard to anonymous namedtuples. The crux of the submission was the idea of instantiating a namedtuple using only Python's generic tuple syntax plus keyword arguments. At the time, the major rebuttal against this idea however was that, since kwargs are an unordered collection, it would be non-trivial to figure out a proper convention for mapping an unordered collection onto an ordered one.
Times have changed however, and with python3.6/3.7 we now have the guarantee that dictionaries are insertion ordered. This means that by extension keyword arguments are also ordered, and thus the major point of contest against anonymous namedtuples is now moot. And as such I would like to once again suggest anonymous namedtuples as a potential PEP.
Proposal ======= The proposal ultimately is the same as before: ``` my_point = (x=12, y=16) # (x=12, y=16) my_point[0] # 12 ```
where kwargs can be passed to a tuple's instantiation [either via tuple() or just ()], and these tuples would have all the same properties of a namedtuple.
A basic, pure-python implementation is actually quite trivial now given that kwargs are ordered. Observe: ``` def atuple(**kwargs): signature = " ".join(kwargs) _type = atuple.__dict__.get(signature) if not _type: _type = namedtuple(atuple.__name__, signature) atuple.__dict__[signature] = _type return _type(**kwargs) ``` This has none of the problems suggested in the previous rebuttal.
anonymous tuples of incorrect ordering raise the assertion error, as expected:
```
assert atuple(x=12, y=16) == atuple(y=16, x=12) Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError
...and by extension it preserves the users's specified ordering as given by **kwargs.
pt = atuple(r=3, theta=0.5, phi=0.25) pt atuple(r=3, theta=0.5, phi=0.25)
Given that this implementation caches the type by the keyword signature, anonymous tuples of the same signature are recognized as the same type. However, given differing values, they are not equal.
type(atuple(x=1,y=2)) == type(atuple(x=1,y=2)) True atuple(x=1,y=2) == atuple(x=1,y=3) False
This allows us to provide positional arguments to the construct, just like regular namedtuples, and passing a mismatch throws an error as expected.
pt = atuple(x=12, y=16) pt atuple(x=12, y=16) type(pt)(1,2) atuple(x=1, y=2) type(pt)(1,2,3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: atuple.__new__() takes 3 positional arguments but 4 were given
As we can see, the introduction of insertion-ordered dictionaries solves all of the issues previously raised with regard to anonymous namedtuples. References: [1] https://mail.python.org/pipermail/python-ideas/2016-April/039857.html _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/EUJTGE2VIHHMNJN574L5BEAS7ROSTYZL/ Code of Conduct: http://python.org/psf/codeofconduct/
-- Thanks, Andrew Svetlov
Well you have the values as they're passed in; just (optionally?) include the type in the signature if you want type differentiation
def anamedtuple(**kwargs): ... signature = " ".join([f"{k}: {type(v)}" for k, v in kwargs.items()]) ... _type = anamedtuple.__dict__.get(signature) ... if not _type: ... _type = namedtuple(anamedtuple.__name__, tuple(kwargs)) ... anamedtuple.__dict__[signature] = _type ... return _type(**kwargs) ... type(anamedtuple(x=1)) == type(anamedtuple(x='a')) False
And I would disagree with the idea that memory growth would be "uncontrollable" - new types are generated only when a developer requests a new type, and type generation in this case is idempotent, since types are cached.
On 3/23/2022 5:03 PM, michael@michaelgreen.dev wrote:
Back in early 2016 there was an idea submitted[1] by Joseph Martinot-Lagarde in regard to anonymous namedtuples. The crux of the submission was the idea of instantiating a namedtuple using only Python's generic tuple syntax plus keyword arguments. At the time, the major rebuttal against this idea however was that, since kwargs are an unordered collection, it would be non-trivial to figure out a proper convention for mapping an unordered collection onto an ordered one.
Times have changed however, and with python3.6/3.7 we now have the guarantee that dictionaries are insertion ordered. This means that by extension keyword arguments are also ordered, and thus the major point of contest against anonymous namedtuples is now moot. And as such I would like to once again suggest anonymous namedtuples as a potential PEP.
Proposal ======= The proposal ultimately is the same as before: ``` my_point = (x=12, y=16) # (x=12, y=16) my_point[0] # 12 ``` ... As we can see, the introduction of insertion-ordered dictionaries solves all of the issues previously raised with regard to anonymous namedtuples.
I don't think this is justifiable as built-in behavior. Just rename your function "t", then you can write t(x=12, y=16). That fact that it's not typing friendly is definitely as strike against it, in my opinion. But if you really want to pursue it, it would definitely need a PEP, and someone to sponsor it. Eric
On Thu, Mar 24, 2022 at 4:44 PM Eric V. Smith <eric@trueblade.com> wrote:
I don't think this is justifiable as built-in behavior. Just rename your function "t", then you can write t(x=12, y=16).
If nothing else, a good way to prototype it and see if folks like it. I agree that there may be little reason to make this part of the builtin tuple.
That fact that it's not typing friendly is definitely as strike against it, in my opinion.
not in my opinion -- Python is not a statically typed language, I will be really sad if the future of Python development was motivated by ease of static typing. Now that I think about, namedtupled are not typing friendly either -- part of their point is that you can use one everywhere a tuple is expected. As soon as you type something as expecting a particular namedtuple, you've just broken that. As for the memory growth -- would it need to be a new type? yes, namedtuples are types, but could we have a "tuple_with_attribute_names" that wasn't a distinct type, but rather, dynamically accessed its fields? I suppose that would be a lot heavier weight than a tuple, so maybe the same issue. -CHB -- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
On 2022-03-24 10:43, Andrew Svetlov wrote:
The proposal doesn't work well with type hints: atuple(a=1, b=2) and atuple(a="a", b="b") generates the same type.
I'm neither here nor there on the original proposal, but I just want to push back against this reasoning. Type hints are an entirely optional feature of Python which do not affect the runtime behavior of Python code in any way (except insofar as other code explicitly reads the annotations), and this has been explicitly reiterated multiple times (e.g., in PEPs). In my view, how something interacts with typing has no relevance in evaluating a proposal such as this. To allow typing considerations to constrain Python's future development in any way whatsoever is to slide down the slippery slope which was explicitly disavowed in PEP 484, namely a gradual move towards a de facto "requirement" for static typing. I would most heartily prefer to avoid that. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown
Agreed w/r/t the issue of typing, glad I wasn't the only one who was feeling that way. Maybe a reasonable approach would be to potentially add this as an option to the collections module? Either as an isolated function (`anamedtuple` perhaps) or add control flow to `namedtuple` such that when only kwargs are provided, return an anonymous namedtuple directly. Or both? :shrug: Id want to try to do most of the work via the C/Python API, my original `def` is on par with calling an instance of `namedtuple`, but both are an order of magnitude slower than simply creating a tuple, which I think is unacceptable as a construct that I would want to use extensively in a codebase. Hell, even instantiating a generic object is twice as fast. If I wanted to submit this as a pep (or even just submit this for a potential sponsorship), is the best way to go about it to work directly in a clone of the c/python repo, or to write a standalone library first and then after preliminary approval, add it to c/python?
If I wanted to submit this as a pep (or even just submit this for a potential sponsorship), is the best way to go about it to work directly in a clone of the c/python repo, or to write a standalone library first and then after preliminary approval, add it to c/python?
You don’t need an implementation to write a PEP, and certainly not to get a sponsor. That being said, a prototype can be helpful to work out the issues. You could go either way, but if it can be stand-alone, that’s easier, especially for others to try it out. -CHB
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/Q6JTO3... Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
Finally got around to fleshing out my idea here. My thought was to make an anonymous names tuple not as a new anonymous class, but simply as an instance with specific field names. So all of these would the the same class, but they would also be lightweight, and all subclasses of tuple -- from a typing perspective, it's just be a tuple - and could be used anywhere a tuple could. Here's a quick prototype I came up with: class TupleWithNames(tuple): def __new__(cls, **fields): self = tuple.__new__(cls, fields.values()) for name, val in fields.items(): self.__dict__[name] = val return self def __setattr__(self, attr, val): raise AttributeError("Can't set attributes on TupleWithNames") def __repr__(self): return f"TupleWithNames({', '.join(f'{k}={v}' for k, v in self.__dict__.items())})" In [172]: point = TupleWithNames(x=5, y=10) In [173]: point Out[173]: TupleWithNames(x=5, y=10) In [174]: point.x Out[174]: 5 In [175]: point.y Out[175]: 10 In [176]: point.x = 2 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-176-6a65d3605158> in <module> ----> 1 point.x = 2 ~/PythonStuff/tuplewithnames/tuplewithnames.py in __setattr__(self, attr, val) 11 12 def __setattr__(self, attr, val): ---> 13 raise AttributeError("Can't set attributes on TupleWithNames") 14 15 def __repr__(self): AttributeError: Can't set attributes on TupleWithNames In [177]: point.z = 3 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-177-902df67327f4> in <module> ----> 1 point.z = 3 ~/PythonStuff/tuplewithnames/tuplewithnames.py in __setattr__(self, attr, val) 11 12 def __setattr__(self, attr, val): ---> 13 raise AttributeError("Can't set attributes on TupleWithNames") 14 15 def __repr__(self): AttributeError: Can't set attributes on TupleWithNames In [178]: point[0] Out[178]: 5 In [179]: point[1] Out[179]: 10 In [180]: point[1] = 3 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-180-d6d4f8cfca2f> in <module> ----> 1 point[1] = 3 TypeError: 'TupleWithNames' object does not support item assignment So it's a (subclass of) tuple -- you can access the entries by name, you can't change them. It's truly immutable, 'cause the __dict __ is there to mess with, but that (I think) could be fixed if it were implemented in C. This really simple version hashes as the tuple -- so it's the same as a tuple with the same values, or a TupleWithNames with the same values, and any arbitrary names. Which I think is OK -- the idea here is that it IS just like a tuple except you can use the field names to access items for readability. But you could customize the hash, too if you wanted. Anyway -- that's in idea -- details to be fleshed out if anyone wanted to pursue this. -CHB On Sat, Mar 26, 2022 at 1:49 PM Christopher Barker <pythonchb@gmail.com> wrote:
If I wanted to submit this as a pep (or even just submit this for a
potential sponsorship), is the best way to go about it to work directly in a clone of the c/python repo, or to write a standalone library first and then after preliminary approval, add it to c/python?
You don’t need an implementation to write a PEP, and certainly not to get a sponsor.
That being said, a prototype can be helpful to work out the issues.
You could go either way, but if it can be stand-alone, that’s easier, especially for others to try it out.
-CHB
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/Q6JTO3... Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD (Chris)
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
-- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
I think I'd often reach for an optimized version of something like this over SimpleNamespace or even a Dataclass if it existed. On Tue, Mar 29, 2022, 2:04 AM Christopher Barker <pythonchb@gmail.com> wrote:
Finally got around to fleshing out my idea here.
My thought was to make an anonymous names tuple not as a new anonymous class, but simply as an instance with specific field names. So all of these would the the same class, but they would also be lightweight, and all subclasses of tuple -- from a typing perspective, it's just be a tuple - and could be used anywhere a tuple could.
Here's a quick prototype I came up with:
class TupleWithNames(tuple): def __new__(cls, **fields): self = tuple.__new__(cls, fields.values()) for name, val in fields.items(): self.__dict__[name] = val return self
def __setattr__(self, attr, val): raise AttributeError("Can't set attributes on TupleWithNames")
def __repr__(self): return f"TupleWithNames({', '.join(f'{k}={v}' for k, v in self.__dict__.items())})" In [172]: point = TupleWithNames(x=5, y=10)
In [173]: point Out[173]: TupleWithNames(x=5, y=10)
In [174]: point.x Out[174]: 5
In [175]: point.y Out[175]: 10
In [176]: point.x = 2 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-176-6a65d3605158> in <module> ----> 1 point.x = 2
~/PythonStuff/tuplewithnames/tuplewithnames.py in __setattr__(self, attr, val) 11 12 def __setattr__(self, attr, val): ---> 13 raise AttributeError("Can't set attributes on TupleWithNames") 14 15 def __repr__(self):
AttributeError: Can't set attributes on TupleWithNames
In [177]: point.z = 3 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-177-902df67327f4> in <module> ----> 1 point.z = 3
~/PythonStuff/tuplewithnames/tuplewithnames.py in __setattr__(self, attr, val) 11 12 def __setattr__(self, attr, val): ---> 13 raise AttributeError("Can't set attributes on TupleWithNames") 14 15 def __repr__(self):
AttributeError: Can't set attributes on TupleWithNames
In [178]: point[0] Out[178]: 5
In [179]: point[1] Out[179]: 10
In [180]: point[1] = 3 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-180-d6d4f8cfca2f> in <module> ----> 1 point[1] = 3
TypeError: 'TupleWithNames' object does not support item assignment
So it's a (subclass of) tuple -- you can access the entries by name, you can't change them.
It's truly immutable, 'cause the __dict __ is there to mess with, but that (I think) could be fixed if it were implemented in C.
This really simple version hashes as the tuple -- so it's the same as a tuple with the same values, or a TupleWithNames with the same values, and any arbitrary names.
Which I think is OK -- the idea here is that it IS just like a tuple except you can use the field names to access items for readability. But you could customize the hash, too if you wanted.
Anyway -- that's in idea -- details to be fleshed out if anyone wanted to pursue this.
-CHB
On Sat, Mar 26, 2022 at 1:49 PM Christopher Barker <pythonchb@gmail.com> wrote:
If I wanted to submit this as a pep (or even just submit this for a
potential sponsorship), is the best way to go about it to work directly in a clone of the c/python repo, or to write a standalone library first and then after preliminary approval, add it to c/python?
You don’t need an implementation to write a PEP, and certainly not to get a sponsor.
That being said, a prototype can be helpful to work out the issues.
You could go either way, but if it can be stand-alone, that’s easier, especially for others to try it out.
-CHB
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/Q6JTO3... Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD (Chris)
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
-- Christopher Barker, PhD (Chris)
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/I6VTNH... Code of Conduct: http://python.org/psf/codeofconduct/
On Fri, Mar 25, 2022 at 12:17 AM Brendan Barnwell <brenbarn@brenbarn.net> wrote:
On 2022-03-24 10:43, Andrew Svetlov wrote:
The proposal doesn't work well with type hints: atuple(a=1, b=2) and atuple(a="a", b="b") generates the same type.
I'm neither here nor there on the original proposal, but I just want to push back against this reasoning. Type hints are an entirely optional feature of Python which do not affect the runtime behavior of Python code in any way (except insofar as other code explicitly reads the annotations), and this has been explicitly reiterated multiple times (e.g., in PEPs). In my view, how something interacts with typing has no relevance in evaluating a proposal such as this. To allow typing considerations to constrain Python's future development in any way whatsoever is to slide down the slippery slope which was explicitly disavowed in PEP 484, namely a gradual move towards a de facto "requirement" for static typing. I would most heartily prefer to avoid that.
strong +1
-- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/L67XX3... Code of Conduct: http://python.org/psf/codeofconduct/
@Michael: As the previous author I fully support this proposal! ;) Thank you for the revival. For reference, since you linked one of the answer of the original thread, this is my original proposal: https://mail.python.org/pipermail/python-ideas/2016-April/039836.html Both suggested implementations are very interesting. If I'm not wrong they have exactly the same behavior (except for __dict__). Performance-wise with a quick test I get the following results: t = (1, 2) print("sizeof", sys.getsizeof(t)) # 56 print("sizeof type", sys.getsizeof(type(t))) # 408 print(timeit.timeit("(1, 2)")) # ~0.01 nt = namedtuple("nt", ["x", "y"]) print("sizeof", sys.getsizeof(nt(1, 2))) # 56 print("sizeof type", sys.getsizeof(type(nt(1, 2)))) # 896 print(timeit.timeit("nt(1, 2)", setup="from __main__ import nt")) # ~0.2 pt = atuple(x=1, y=2) print("sizeof", sys.getsizeof(pt)) # 56 print("sizeof type", sys.getsizeof(type(pt))) # 896 print(timeit.timeit("atuple(x=12, y=16)", setup="from __main__ import atuple")) # ~0.8 point = TupleWithNames(x=1, y=2) print("sizeof", sys.getsizeof(point)) # 64 print("sizeof type", sys.getsizeof(type(point))) # 1064 print(timeit.timeit("TupleWithNames(x=12, y=16)", setup="from __main__ import TupleWithNames")) # ~0.8 The timing performance of both solutions is roughly the same given the measurament variation, but way slower than tuple an namedtuple. TupleWithNames is a bit more memory hungry than atuple and namedtuple, but there is only one type so if I understand correctly it would be a win for multiple instances. Maybe there is room for optimisation in both cases ?
On Tue, Mar 29, 2022 at 6:37 AM Joseph Martinot-Lagarde < contrebasse@gmail.com> wrote:
Both suggested implementations are very interesting. If I'm not wrong they have exactly the same behavior (except for __dict__).
pretty close, yes -- probably differences with pickling, and if you did an type checking, very different. Performance-wise with a quick test I get the following results:
t = (1, 2) print("sizeof", sys.getsizeof(t)) # 56 print("sizeof type", sys.getsizeof(type(t))) # 408 print(timeit.timeit("(1, 2)")) # ~0.01
nt = namedtuple("nt", ["x", "y"]) print("sizeof", sys.getsizeof(nt(1, 2))) # 56 print("sizeof type", sys.getsizeof(type(nt(1, 2)))) # 896 print(timeit.timeit("nt(1, 2)", setup="from __main__ import nt")) # ~0.2
pt = atuple(x=1, y=2) print("sizeof", sys.getsizeof(pt)) # 56 print("sizeof type", sys.getsizeof(type(pt))) # 896 print(timeit.timeit("atuple(x=12, y=16)", setup="from __main__ import atuple")) # ~0.8
point = TupleWithNames(x=1, y=2) print("sizeof", sys.getsizeof(point)) # 64 print("sizeof type", sys.getsizeof(type(point))) # 1064 print(timeit.timeit("TupleWithNames(x=12, y=16)", setup="from __main__ import TupleWithNames")) # ~0.8
you have to be careful with getsizeof() -- I don't think it digs into nested objects. Anyway, it would certainly require a close look. The timing performance of both solutions is roughly the same given the
measurament variation,
That surprises me -- a tuple has to call namedtuple, and there's a lot of work done in there -- very odd. I guess that's why we have to profile, but I'd still give that a closer look.
but way slower than tuple an namedtuple.
well, with namedtuple, you aren't taking into account the creation of the class -- so if you make thousands of the same one, yes, but if you make only a couple, then not so much.
TupleWithNames is a bit more memory hungry than atuple and namedtuple, but there is only one type so if I understand correctly it would be a win for multiple instances.
I think so -- at least of atuple -- though one could create a cache of namedtuples so that atuple would reuse an existing one if was already there. Maybe there is room for optimisation in both cases ?
I"m sure -- namedtuple use sys.intern on the names, that would help. and I tried to use __slots__ in TupleWithNames (sorry for that horrible name ;-) ), but apparently you can't use __slots__ in a tuple subclass ('cause tuple's already using it ??) -- but that could be done in a builtin. then it wouldn't need a __dict__ There's also various options for storing the fields -- I only tried the first one I thought of. This reminds me -- it would be kinda cool if there was an easier, more robust way to make an immutable in Python -- maybe a frozendict for __dict__? Anyway, if someone wants to take this further, I'd be glad to help. -CHB -- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
On 3/29/22 09:14, Christopher Barker wrote:
[...] I tried to use __slots__ in TupleWithNames, but apparently you can't use __slots__ in a tuple subclass ('cause tuple's already using it ??) -- but that could be done in a builtin. then it wouldn't need a __dict__
The `__dict__` is needed to store the field names -- did you add `__dict__` to the `__slots__`? (Of course, if you've added `__dict__` then you lose the limited size of `__slots__`.) -- ~Ethan~
On Tue, Mar 29, 2022 at 12:49 PM Ethan Furman <ethan@stoneleaf.us> wrote:
On 3/29/22 09:14, Christopher Barker wrote:
[...] I tried to use __slots__ in TupleWithNames, but apparently you can't use __slots__ in a tuple subclass ('cause tuple's already using it ??) -- but that could be done in a builtin. then it wouldn't need a __dict__
The `__dict__` is needed to store the field names -- did you add `__dict__` to the `__slots__`? (Of course, if you've added `__dict__` then you lose the limited size of `__slots__`.)
-- ~Ethan~
Maybe I'm being an ignoramus but: how would it be possible to even use slots? Slots are descriptors living in the class namespace. You don't know ahead of time what the member names are, so you can't use slots, right? Unless you modify the TupleWithNames class on the fly, adding slot names as they are needed. But then you need some kind of machinery for each instance to know which slots are valid for that instance, and to block access to slots that aren't part of that instance. Again, I might just be a doof. --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler
On 3/29/22 09:57, Ricky Teachey wrote:
On Tue, Mar 29, 2022 at 12:49 PM Ethan Furman wrote:
The `__dict__` is needed to store the field names -- did you add `__dict__` to the `__slots__`? (Of course, if you've added `__dict__` then you lose the limited size of `__slots__`.)
Maybe I'm being an ignoramus but: how would it be possible to even use slots? Slots are descriptors living in the class namespace. You don't know ahead of time what the member names are, so you can't use slots, right?
You can use them, just make sure one of the slots is `__dict__`. Of course, by adding `__dict__` you lose most (all?) of the advantages of using `__slots__` in the first place. -- ~Ethan~
The `__dict__` is needed to store the field names -- did you add `__dict__` to the `__slots__`?
Nope — though the error indicated that I couldn’t add anything to __slots__ when subclassing tuple. But I may have misunderstood the error. Also, frankly, I’m completely confused by __slots__ : looking at the namedtuple implementation, it sets slots to an empty tuple. But it does have instance attributes without a __dict__.
Maybe I'm being an ignoramus but: how would it be possible to even use slots? Slots
are descriptors living in the class namespace. You don't know ahead of time what the member names are, so you can't use slots, right?
The idea there is that you’d store the field names in a slot, and dynamically return the values in __getattr__. In that case, frozen dict would be nice :-) Anyway — good idea? I don’t know, ‘cause I did get it to work :-) -CHB
You can use them, just make sure one of the slots is `__dict__`.
Of course, by adding `__dict__` you lose most (all?) of the advantages of using `__slots__` in the first place.
-- ~Ethan~ _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/QM7MIH... Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
On 3/29/22 14:41, Christopher Barker wrote:
Ethan Furman queried:
The `__dict__` is needed to store the field names -- did you add `__dict__` to the `__slots__`?
Nope — though the error indicated that I couldn’t add anything to __slots__ when subclassing tuple. But I may have misunderstood the error.
Ah, that's right -- it's saying that *instances* cannot have anything in `__slots__`, but the class can still have fields: >>> class Tuple(tuple): ... this = 'eggs' ... that = 'spam' ... __slots__ = () ... def __new__(cls, *args): ... return tuple.__new__(cls, *args) ... >>> t = Tuple((1,)) >>> t (1,) >>> t.this 'eggs' >>> t.that 'spam' So the Tuple class would have properties that redirect to the correct offset -- but that would require a different tuple class for each field configuration. -- ~Ethan~
participants (10)
-
Andrew Svetlov
-
Brendan Barnwell
-
Christopher Barker
-
Eric V. Smith
-
Ethan Furman
-
Joao S. O. Bueno
-
Joseph Martinot-Lagarde
-
Michael Green
-
michael@michaelgreen.dev
-
Ricky Teachey