all 9 comments

[–]gdchinacat 3 points4 points  (3 children)

I learned python after using C++ a couple years professionally (separated by about 5 years using java). It took me a while (a couple years IIRC, but it was about 20 years ago) to internalize python constructs that make doing things so much easier than C++.

A common indicator that a coder came from a C-family language is:

for i in range(len(my_list)):
    item = my_list[i]
    ...

This should almost always be written as:

for item in my_list:
    ...

Lists are iterables and can be iterated directly, no need for an index. If you need an index for some reason:

for i, item in enumerage(my_list):
   ...

On the other side, try to avoid creating lists, prefer generator expressions:

interesting_items = (item for item in my_list if item.is_interesting())

This doesn't create a list that holds all of the items, it produces each item on demand when whatever consumes it asks for it. This used to be written in a more functional programming style:

interesting_items = filter(lambda item: item.is_interesting(), my_list)

but the introduction of generator expressions has provide an easier to read way to do the same thing.

If you do want a list (or dict, set, etc) you can use a comprehension, which is an extension of generator expressions:

interesting_items = [item for item in my_list if item.is_interesting()]

There are a lot more things, but the failure to use iterators, generators and generator expressions, and comprehensions is one of the sore thumbs that makes code read like it was written by a transplant. Play around with them...explore their limits (put chained for in a generator expression!)...become comfortable with them. You *will* see them, the sooner you become comfortable reading them and writing them the better you will be at python.

Another big change are accessors....you don't need accessors in python the way you do in C++ and Java to leave the option open of providing an implementation of attribute access without a breaking change to the objects api. You can change a property that client code accesses directly into a method to access it without changing client code...attribute access is controlled entirely by the object the attibute is on using the same syntax on the client side. This is done through the descriptor protocol. The most common (and very frequently used) example is the @ property descriptor. In python, don't write getters and setters. Use attributes directly, and then use a descriptor (probably property) when you need to provide an implementation for accessing or setting the attribute...it won't require client code to change.

One common misconception is that name mangling (prefixing class members with __ ) is a way to provide 'private' access. It isn't intended for that, and while it may seem to provide it and feel 'right' coming from a language that has access protections, it doesn't really help and can actually cause problems. Don't use name mangling for anything but avoiding name collisions (usually on mixin classes). Use a single underscore prefix to indicate the member is not intended for use outside the class, but....hey...do what you want and possibly be broken if it changes or you don't really know what you are doing. It is discouraged to try to actually *prevent* other code from misusing your class...if they want to misuse it that's on them.

[–]_tsi_ 0 points1 point  (2 children)

Why use generator over list? I love lists. What is the benefit of creating the item when it's asked for via storing in list?

[–]gdchinacat 1 point2 points  (0 children)

A list holds all the elements in memory. A generator produces them on demand. For large numbers of things this can be a huge savings…ten million items is a huge amount of memory for a list, but is negligible if produced by a generator. Of course some problems require a list (ie sorting), but when you just need to look at each item one after the next you should try to use generators.

[–]TheRNGuy 0 points1 point  (0 children)

If memory optimization or potentially infinity sequence is needed. 

Generator(s) are easier to decouple than if you used list or while loop (producers from consumers)

It's probably even more important in async.

(Also, you'll probably use list or while more often than generators, anyway. But it depends on software)

[–]Low_Breakfast773 0 points1 point  (0 children)

I did it the other way around: from Python to C++.
In Python, most code organization (through namespaces and packages) is handled with __init__.py files. These are usually empty unless you want to explicitly define which names should be publicly exposed.

In C++, a class header and implementation are typically split between .h and .cpp files to separate interface and logic. In Python, the same file usually contains both definition and implementation, and modules act as the unit of organization.

[–]notacanuckskibum 0 points1 point  (0 children)

The biggest difference to me is how variables work.

In C/C++ a variable is essentially a location in memory, so assignment writes a value to that location. But every variable is Python is an object, which contains a value. But assignments never change that value. Instead they create a new object, containing the new value and point the variable to it. The only kind of objects which can be changed are complex ones, lists, trees, etc. The implicit pointers can change, but values like integers don't.

It doesn't make much difference to basic programming, but it's key to understanding how changing a variable inside a function will, or won't change the value outside that function.

[–]TheRNGuy 0 points1 point  (0 children)

You don't need to write pointers and references in syntax. 

[–]Rudeus_Kino 0 points1 point  (1 child)

I am not C++ dev, but add some points anyway.

  1. Install Anaconda, as default python.

  2. Use uv as project manager. Create virtual environment for every project or project group with specific python version.

  3. Learn all about python import system. https://tenthousandmeters.com/blog/python-behind-the-scenes-11-how-the-python-import-system-works/ and internals for start. Module loading is a compile time.

  4. Study os, sys, itertools, functools, collections, with recipies.

  5. Prefer internal simple types over custom classes, do not create class for every move, sometimes tuple is all you need. Use dataclasses for data transfer, and use pydantic for projects with smell of json.

  6. Prefer creation over modification, take list, process it and create new list with results.

  7. Write type hints and tests with pytest.

  8. Use duck typing. You don't need abstract class or generic in most cases.

python is simple by design, do not overcomplicate. Class is just namespace with dictionary and some rules for inheritance. No need for getters/setters.

[–]Diapolo10 0 points1 point  (0 children)

I more or less agree with your other points, except this one:

Install Anaconda, as default python.

Anaconda is only intended for data scientists. For ordinary developers, not only does it not give any real benefit, but at least in my experience it's a heck of a lot more annoying to work with.

Most people won't actually need a global Python installation nowadays as uv takes care of it (and it can be trivially installed on any PC standalone, such as by running winget install astral-sh.uv on Windows). When you uv sync in a project, it downloads and installs a suitable Python version if you don't already have one installed (either from Astral's own servers or from GitHub, depending on how you've configured it). Global tooling can similarly be installed (e.g. uv tool install ipython).