List replication operator
Rob Gaddi
rgaddi at highlandtechnology.invalid
Fri May 25 12:58:19 EDT 2018
On 05/25/2018 04:53 AM, Steven D'Aprano wrote:
> On Fri, 25 May 2018 09:28:01 +0200, Peter Otten wrote:
>
>> Yet another arcanum to learn for beginners with little return. If you
>> cannot refrain from tinkering with the language at least concentrate on
>> the features with broad application. Thank you.
>
> Broader than multi-dimensional arrays? There are a bazillion uses for
> them. How many do you need before it is "broad application"?
>
> [snip]
>
> This is a frequent, recurring pain point. Experienced programmers forget
> how confusing the behaviour of * is because they're so used to the
> execution model. They forget that writing a list comp is not even close
> to obvious, not only for beginners but even some experienced Python
> programmers.
>
I agree that it's non-obvious, but I disagree with your diagnosis. The
non-obvious bit is that the empty list is a reference to a newly created
mutable, not an immutable constant. Or, more the point, that the word
"mutable" is one that you need to know and think about in the first place.
The thing tripping the young pups is not understanding the underlying
Python concepts of objects and their references. And I sympathize;
Python's handling there is seriously non-trival. I guarantee everyone
on this list got bit by it when they were starting out, either in this
case or as a default argument. I probably still manage to screw it up a
couple times a year due to not thinking about it clearly enough.
So, in the spirit of explicit being better than implicit, please assume
that for actual implementation replicate would be a static method of
actual list, rather than the conveniently executable hackjob below.
_list = list
_nodefault = object()
class list(_list):
@staticmethod
def replicate(*n, fill=_nodefault, call=list):
"""Return a list of specified dimensions.
Fill and call can be used to prime the list with initial values, the
default is to create a list of empty lists.
Parameters:
n : List of dimensions
fill: If provided, the fill object is used in all locations.
call: If fill is not provided, the result of call (a function of
no arguments) is used in all locations.
Example:
>>> a = list.replicate(2, 3)
>>> a
[[[], []], [[], []], [[], []]]
>>> a[0][0].append('a')
>>> a
[[['a'], []], [[], []], [[], []]]
>>> b = list.replicate(2, 3, fill=[])
>>> b
[[[], []], [[], []], [[], []]]
>>> b[0][0].append('a')
>>> b
[[['a'], ['a']], [['a'], ['a']], [['a'], ['a']]]
>>> c = list.replicate(2, 3, call=dict)
>>> c
[[{}, {}], [{}, {}], [{}, {}]]
>>> c[0][0]['swallow'] = 'unladen'
>>> c
[[{'swallow': 'unladen'}, {}], [{}, {}], [{}, {}]]
>>> d = list.replicate(2, 3, fill=0)
>>> d
[[0, 0], [0, 0], [0, 0]]
>>> d[0][0] = 5
>>> d
[[5, 0], [0, 0], [0, 0]]
"""
if n:
this = n[-1]
future = n[:-1]
return [
list.replicate(*future, fill=fill, call=call)
for _ in range(this)
]
elif fill is _nodefault:
return call()
else:
return fill
--
Rob Gaddi, Highland Technology -- www.highlandtechnology.com
Email address domain is currently out of order. See above to fix.
More information about the Python-list
mailing list