Bikeshedding:
1) I forgot to mention whether the '&' operator should apply to byte
strings as well as to strings.
I propose that it should (some supporting examples below).
2) Joao suggested spelling the operator '|'.
But for me: '|' suggests "or" while '&' suggests "and" and is a
more intuitive choice for some kind of concatenation.
On 06/03/2023 10:33, Steven D'Aprano wrote:
I find the concept is very easy to understand: "concat with exactly one
space between the operands". But I must admit I'm struggling to think
of cases where I would use it.
Well, there are use cases for print() separating its operands with
single spaces (by default). So one might guess that something similar
would be useful when constructing strings. But read on, Gentle Reader ...
I like the look of the & operator for concatenation, so I want to like
this proposal. But I think I will need to see real world code to
understand when it would be useful.
Quite right, Steven. (By the way, thanks for reading my suggestion
carefully, as you obviously did.)
Below I list some examples from the 3.8.3 stdlib and how (I think) they
could be rewritten.
(Disclosure: I have selected the examples I found which I think are most
convincing. Then again, I may have missed some.)
I say "I think" because to be 100% sure (rather than 90%+ sure) in all
cases would sometimes need a thorough analysis of the values that parts
of an expression could (reasonably) take: specifically, whether some
could be an empty string or all whitespace and what would happen if they
were. Often in practice these cases can be disregarded or are
unimportant. And of course the code author should know all about these
cases.
Arguably, good examples are *under-represented* in the stdlib, because
'&' will more often be useful in throw-away diagnostic code (which is
all about human-readable text) than in production code.
Lib\site-packages\numpy\distutils\system_info.py:2677:
cmd = config_exe + ' ' + self.append_config_exe + ' ' + option
cmd = config_exe & self.append_config_exe & option
Lib\site-packages\wx\lib\masked\maskededit.py:3592:
newstr = value[:self._signpos] + ' ' +
value[self._signpos+1:-1] + ' '
newstr = value[:self._signpos] & value[self._signpos+1:-1] + ' '
Lib\site-packages\wx\lib\masked\maskededit.py:4056:
text = text[:self._signpos] + ' ' +
text[self._signpos+1:self._right_signpos] + ' ' +
text[self._right_signpos+1:]
text = text[:self._signpos] &
text[self._signpos+1:self._right_signpos] & text[self._right_signpos+1:]
Lib\distutils\sysconfig.py:212:
ldshared = ldshared + ' ' + os.environ['LDFLAGS']
ldshared = ldshared & os.environ['LDFLAGS']
There are 6 more similar examples in the same module.
The same 7 are in Lib\site-packages\setuptools\_distutils\sysconfig.py.
Lib\site-packages\numpy\lib\tests\test_io.py:328-330:
assert_equal(c.readlines(),
[asbytes((fmt + ' ' + fmt + '\n') % (1, 2)),
asbytes((fmt + ' ' + fmt + '\n') % (3, 4))])
assert_equal(c.readlines(),
[asbytes((fmt & fmt + '\n') % (1, 2)), 2)),
asbytes((fmt & fmt + '\n') % (3, 4))])
Lib\site-packages\numpy\f2py\crackfortran.py:1068:
t = typespattern[0].match(m.group('before') + ' ' + name)
t = typespattern[0].match(m.group('before') & name)
Lib\site-packages\twisted\mail\imap4.py:3606:
raise IllegalServerResponse('(' + k + ' '+ status[k] + '): ' +
str(e))
raise IllegalServerResponse('(' + k & status[k] + '):' & str(e))
Lib\site-packages\twisted\runner\procmon.py:424-426:
return ('<' + self.__class__.__name__ + ' '
+ ' '.join(l)
+ '>')
return ('<' + self.__class__.__name__
& ' '.join(l)
+ '>')
Lib\site-packages\wx\lib\pydocview.py:3028:
label = '&' + str(i + 1) + ' ' + frame.GetTitle()
label = '&' + str(i + 1) & frame.GetTitle()
Lib\test\test_pyexpat.py:90-91:
self.out.append('Start element: ' + repr(name) + ' ' +
sortdict(attrs))
self.out.append('Start element:' & repr(name) &
sortdict(attrs))
Lib\test\test_pyexpat.py:102:
self.out.append('PI: ' + repr(target) + ' ' + repr(data))
self.out.append('PI:' & repr(target) & repr(data))
Lib\test\test_pyexpat.py:105:
self.out.append('NS decl: ' + repr(prefix) + ' ' + repr(uri))
self.out.append('NS decl:' & repr(prefix) & repr(uri))
In the above 3 examples, it is not necessary to replace the first '+' by
'&',
but I think it reads better to use the same operator both times.
(And of course it saves 1 (space) character. 😁)
Similarly in some other examples.
Lib\site-packages\win32\Demos\security\sspi\fetch_url.py:57:
h.putheader('Authorization', auth_scheme + ' ' + auth)
h.putheader('Authorization', auth_scheme & auth)
Tools\scripts\which.py:51:
sts = os.system('ls ' + longlist + ' ' + filename)
sts = os.system('ls' & longlist & filename)
Less obviously:
Lib\site-packages\pycparser\c_generator.py:406-407:
nstr = '* %s%s' % (' '.join(modifier.quals),
' ' + nstr if nstr else '')
nstr = '*' & ' '.join(modifier.quals) & nstr
Examples where (I think) '&' could be applied to byte strings:
Lib\site-packages\twisted\conch\ssh\keys.py:1279:
return (self.sshType() + b' ' + b64Data + b' ' + comment).strip()
return (self.sshType() & b64Data & comment).strip()
Lib\site-packages\reportlab\pdfbase\pdfmetrics.py:391:
text = text + b' ' + bytes(str(self.widths[i]),'utf8')
text = text & + bytes(str(self.widths[i]),'utf8')
Lib\site-packages\twisted\mail\pop3.py:326:
yield intToBytes(i) + b' ' + intToBytes(size) + b'\r\n'
yield intToBytes(i) & intToBytes(size) + b'\r\n'
Lib\site-packages\twisted\mail\pop3.py:367:
yield intToBytes(i + 1) + b' ' + uid + b'\r\n'
yield intToBytes(i + 1) & uid + b'\r\n'
Lib\site-packages\twisted\mail\pop3client.py:929:
return self.sendShort(b'APOP', username + b' ' + digest)
return self.sendShort(b'APOP', username & + digest)
Lib\site-packages\twisted\mail\pop3client.py:1193-1194:
return self._consumeOrAppend(b'TOP', idx + b' ' +
intToBytes(lines),
consumer, _dotUnquoter)
return self._consumeOrAppend(b'TOP', idx & intToBytes(lines),
consumer, _dotUnquoter)
Lib\site-packages\twisted\mail\smtp.py:1647:
r.append(c + b' ' + b' '.join(v))
r.append(c & b' '.join(v))
Lib\site-packages\twisted\mail\_cred.py:33:
return self.user + b' ' + response.encode('ascii')
return self.user & response.encode('ascii')
Lib\site-packages\twisted\web\test\test_proxy.py:233:
lines = [b"HTTP/1.0 " + str(code).encode('ascii') + b' ' + message]
lines = [b"HTTP/1.0" & str(code).encode('ascii') & message]
There are many examples (too many to list) where '&' could be used but
would not add a great deal of value and its use or non-use would be
largely a matter of taste.
(It would be nice if there were always "One Obvious Way To Do It", but
in the real world different tools sometimes overlap in their areas of
application.)
Some would doubtless find it:
Pointless
Obscure
Of course (like any new feature) it *would* be more obscure - until we
got used to it! 😁
Others might welcome (in appropriate use cases):
The guarantee that the result string contains one and only one
space between its textual parts,
even when the first/second operand of '&' contains
(unexpectedly?) trailing/leading whitespace.
This guarantee making the code, in a way, *more* explicit.
An ampersand being more visible than a leading/trailing space
inside string quotes
(as I am finding out the hard way, proof-reading this e-mail!🙁).
(or even) Saving a few characters. (Wash your mouth out with soap,
Rob! 😬)
A few reasonably representative examples:
Lib\cgitb.py:106:
pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
pyver = 'Python' & sys.version.split()[0] + ':' & sys.executable
Lib\ftplib.py:289:
cmd = 'PORT ' + ','.join(bytes)
cmd = 'PORT' & ','.join(bytes)
Lib\site-packages\pythonwin\pywin\tools\browser.py:182:
return str(self.name) + ' (Instance of class ' +
str(self.myobject.__class__.__name__) + ')'
return str(self.name) & '(Instance of class' &
str(self.myobject.__class__.__name__) + ')'
Lib\site-packages\pythonwin\pywin\framework\scriptutils.py:564:
win32ui.SetStatusText('Failed to ' + what + ' - ' + str(details) )
win32ui.SetStatusText('Failed to' & what & '-' & str(details) )
Personally, I think that examples such as the last, where multiple
components are all joined with '&',
are ones that particularly gain from increased clarity and reduced
clutter. YMMV.
Finally: how I might rewrite in Python a sample fragment from my own
code in a different language:
VehDesc.SetLabel(Vehicle.Reg_No & Vehicle.Make & Vehicle.Type &
Vehicle.Colour)
Best wishes
Rob Cliffe
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/7E7HIK3BJIH3YFKE37OKZCAWMBXZIK3T/
Code of Conduct: http://python.org/psf/codeofconduct/