I am porting an old RealBasic program to Python. I’m working on the UI right now, using PySimpleGui. It’s mostly working, but I am having a really weird printing issue. The print statement is just for debugging, but I want to get it working.
I have a combo box that will be used to select a serial port. Right now, the only device on the system is: /dev/cu.Bluetooth-Incoming-Port
When I choose this device, and tell the python code to print the value in the box, I get: /
If I edit the code in any way - add a character to the start of the string, delete a character from the end of the string, add a character to the end of the string, or change a character, it prints correctly. If I edit the string to, say, change the final ’t’ to a ‘p’, the string prints /dev/cu.Bluetooth-Incoming-Porp
If I change that ‘p’ back to a ’t’, it prints: /
It’s almost as if the string is somehow being parsed as the actual device, and not printing correctly when the path is valid.
Any ideas?
It smells a bit like you are getting either a string printed as a single object, or your string is being treated as its basic characters, and you are getting the first character. This can happen when you are expecting a list of strings, and only get one string. Then, when you print the first member of the list, you are surprised when it is only one character.
Try redirecting the output to a file and then bringing up the file in an editor to verify what is actually being printed. There’s a chance that there’s some special characters in the text that is causing the string to look odd in the terminal. The print statement may be printing the whole string, but there may be embedded control characters or something which is causing the terminal to backspace or not print the whole thing. Saving it to a file and then bringing it up in an editor may allow you to see the special characters if they are there. Use an editor like ‘vi’ that will show the non-printable characters.
I got it working.
It was some typical bizarre python data-structure issue. I’m not sure I will ever get the hang of this language.
My original code was:
from serial.tools import list_ports port = list(list_ports.comports()) for p in port: ports = (p.device)
ports then got sent to the combo box, and then printed. I noticed when I plugged in another serial device, I didn’t actually get a list, I only got one device. So, there was clearly some sort of “list” problem.
I changed it back to some earlier code (which had broken due to Python and pySimpleGui updates in the meantime), and this works:
import serial.tools.list_ports from serial.tools.list_ports import comports ports = [p.device for p in comports()]
Forgive me for putting your code sample into a code formatting block. I would normally never alter a quote, but this is absolutely needed for readability.
The “for p in port:” loop isn’t constructing a list of “p.device” port names. “ports” is being overwritten by a scalar value at each iteration, so at the end of the loop it will contain only the last value.
That’s why your original code string worked. That construct creates a list object to assign to “ports”.
Briefly, use the backtick (`) character (unshifted tilde on my keyboard).
So writing `something like this` comes out as something like this.
You can also prepend four spaces to a line to get the same effect.
This can indent as well.
But doing a block of code, it’s easiest to have three backticks by themselves on a line, and then everything after that line will be in a code block until you close with another three backticks.
So:
```
for p in port:
ports = (p.device)
```
(note: I actually have four spaces before the second line of code above)
ends up looking like:
for p in port:
ports = (p.device)
SunUp’s law of markup languages: all Internet markup languages have a way to easily post code snippets with formatting, because all Internet markup languages are designed by programmers who have a use for such markup.
And, optionally, a language after the first set of 3 backticks (after a space) so that Discourse can apply a language-specific parser to colorize the syntax. Which is why my formatted quote recognized that list() was a Python function.
As @gnoitall and @Francis_Vaughan pointed out, your ports variable was pointing to a single string and not to a list of strings.
This is one of the more annoying gotchas in Python. In many languages strings are sequences of characters, but in Python strings are sequences of strings that contain only one character.
As a result you can pass a string to anything that is expecting a list and it will happily, quietly process the ‘list’ one character at time. This is rarely what you want and the logic error you get is usually not like anything you are expecting.
I’ve sort of trained myself that when I get a weird error and the value happens to be the first character of the string, then I assume I fell into this pitfall. It happens more than I’d like to admit.
I think this is my least favorite thing about Python. It makes it easier to write vanilla string processing code, but for everyone else it is a pain. A common pattern is to write a function that accepts either a single item or a list of items. To handle this case, you have to check (a) is this a sequence? and (b) is this not a type of string?. Kind of messy
It’s weird how so many languages have hidden gotchas based on their internal architectures, such that simple assumptions about the meaning of a code snippet may fall apart because what the developer thinks the code means isn’t really what the code means (internally) to the interpreter or compiler.
Yeah, the problem is that there wasn’t a list.
So the thing that is trying to print the list takes the first element of the thing it sees. Which was a string. So you get the first letter.
Python has lists as its core abstraction. Anyone using it needs to get familiar with the way they work.
If you want to create a list directly with a single element you need to add a comma after the element to signal to Python that it is a list with one object and not a naked object. But that isn’t the problem here. Just worth mentioning.
If you want to create a list from some iterative process there are core motifs for that. The list comprehension is the “Pythonic” way.
ports = [p.device for p in port]
This is a very simple use, they can convey at lot of capability in a small space. Say:
I think Duff’s Device fits this description well. We think of switch-case and do-while as each monolithic constructs such that they can’t be interleaved, but that’s not how the compiler thinks of them.
Duffs device is a really interesting idea.
Years ago I was very proud of myself for reinventing the thing when I was trying to get some code optimised.
Then a bit more analysis revealed that it is inherently slower. The trouble being that the jumps require the compiler to save and restore register context around the targets. The fall though in the switch creates a lot of hidden work. This wipes out all the performance gains and more.
Coding at an assembly level you can craft a more efficient version.
Even then you need to consider the effects of branch prediction and speculative execution. You still may well be slower.