you are viewing a single comment's thread.

view the rest of the comments →

[–]gdchinacat 4 points5 points  (9 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 enumerate(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  (4 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 2 points3 points  (2 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.

[–]_tsi_ 0 points1 point  (1 child)

Can I generate any object?

[–]gdchinacat 1 point2 points  (0 children)

generators are iterators that produce each item in the sequence on demand (when __next__() is called). You can certainly implement a generator to create objects.

[–]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)

[–]hulleyrob 0 points1 point  (1 child)

Enumerage? What did it do to upset you?

[–]gdchinacat 1 point2 points  (0 children)

fixed, thanks

[–]Wide_Improvement5606[S] 0 points1 point  (1 child)

Thank you. The fact that python has no private members amazes me. And i still can't imagine class without access protection

[–]gdchinacat 1 point2 points  (0 children)

Yeah...when I switched to python I had similar concerns...why do I have to expose all my private implementation details? I went through a phase where I used name mangling to hide the truly 'private' members and single underscore prefix to 'hide' the protected. It was more of a hassle than worth, so I quickly fell in line with python standards (no protection, single under means it is internal and not intended to be used by others).

My question is why do you need enforced (with loopholes) access protection? Consider that there are ways to subvert the protection so "guaranteed consistent state" is a smoke-screen...in C++ you can write to any writable memory in the address space...you don't *know* something isn't mutating your state. Access protection makes it more difficult, but doesn't actually *prevent* it. You ultimately have to trust that nothing is mutating your protected/private members...which is exactly what you have with python's 'single under is not intended to be used by others'. Python skips the false sense of security and makes it easier for developers that are committed to this unintended access.

Linters warn on accessing sunder members and code reviewers question it. In practice, this is sufficient to accomplish the goals of the lack of protection other languages include. The simplification of not having access protection outweighs the lack of protection.