What’s New In Python 3.6
docs.python.org
docs.python.org
I know some of them have expressed frustration (or more negative feelings) at the way some people have been reacting to their work. As the Python ecosystem is progressively moving to Python 3 (according to some stats, the tipping point should be reached in a few months), I hope that this trend of excellent Python 3 releases will continue.
For starters, Armin Ronacher has written thorough essays on his dislikes. He comments on HN but his influence is obviously much bigger as a Python developer (Flask, etc).
In academia/science, maybe people aren't ripping on 3.x explicitly, But that's because they have the ability to control the work environment and stick with 2.x. They don't rip on 3.x because it's non-existent as far as they are concerned.
Example: http://people.cs.umass.edu/~sheldon/teaching/cs335/
> Programming assignments will use Python, NumPy, and SciPy. The required Python environment is the Anaconda 4.1.1 distribution of Python 2.7. We will not help grade or debug work unless you are working in this environment.
That only means you don't participate in any other online community that discusses Python 3 besides Hacker News.
For instance, the Python2 Vs Python3 migration issues are widely known even for those who never written a single LoC in Python.
Note Prerelease users should be aware that this document is currently in draft form. It will be updated substantially as Python 3.6 moves towards release, so it’s worth checking back even after reading earlier versions.
Although this document is dated today, but it bears the above note right below editors.Also according to PEP 464's Python 3.6 release schedule, Python 3.6.0 final was scheduled on 2016-12-16.
So the title should be changed to "Python 3.6 release candidate 1 released"
- Probably more memory efficient.
>>> import collections
>>> import sys
>>> class A:
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
>>> class B:
... __slots__ = ('x', 'y')
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
>>> C = collections.namedtuple('C', ('x', 'y'))
>>> a = A(1, 1)
>>> b = B(1, 1)
>>> c = C(1, 1)
>>> d = {'x': 1, 'y': 1}
>>> sys.getsizeof(a) + sys.getsizeof(a.__dict__) # Plain object
344
>>> sys.getsizeof(b) + sys.getsizeof(b.__slots__) # Plain object with slots
120
>>> sys.getsizeof(c) # Named Tuple
64
>>> sys.getsizeof(d) # Dict
288
EDIT: Fixed a slew of measurement issuesEDIT2: Included size of __slots__ tuple
Proof:
def make_dicts():
res = []
for i in range(1000000):
res.append({'x': i, 'y': i})
return res
def make_tuples():
templ = namedtuple('Point', ('x', 'y'))
res = []
for i in range(1000000):
res.append(templ(i, i))
return res
from ipython_memory_usage import ipython_memory_usage as imu
imu.start_watching_memory()
used 0.2305 MiB RAM in 4.00s, peaked 0.00 MiB above current, total RAM usage 86.41 MiB
%time tuples = make_tuples()
CPU times: user 780 ms, sys: 32 ms, total: 812 ms
Wall time: 816 ms
used 109.0625 MiB RAM in 0.92s, peaked 0.00 MiB above current, total RAM usage 195.47 MiB
%time dicts = make_dicts()
CPU times: user 160 ms, sys: 32 ms, total: 192 ms
Wall time: 194 ms
used 99.5117 MiB RAM in 0.30s, peaked 0.00 MiB above current, total RAM usage 524.33 MiBIt should not be controversial that namedtuples are clearer for the reason I stated in my initial comment.
To measure real usage, you have to use another method. I use ipython_memory_usage because it's easy to use.
You can test this out easily by making a large dict and see how much your OS thinks python is using vs what sys.getsizeof is reporting.
Many times when a dict is used, it's expected to have certain attributes, and may have expected ways of interacting with those attributes. Named tuples is a simple way to formalize these, which makes them not so mysterious to you future-you, or to other developers who inherit the project 6 months down the road.
pr<TAB> --> print('%cursor%', %cursor%)
nt<TAB> --> namedtuple('%cursor%', ['%cursor%', …]) >>> from collections import namedtuple
>>> Time = namedtuple('Time', ['hours', 'minutes'])
>>> Point = namedtuple('Point', ['x', 'y'])
>>> t = Time(hours=7, minutes=40)
>>> p = Point(x=7, y=40)
>>> p == t
True
They're still tuples. They're meant more as a tool for making an already existing tuple mess slightly more managable rather than creating a new mess.For making plain data objects take less boilerplate to create have a look at attrs: https://attrs.readthedocs.io
I use a lot of classes.
It feels like Java sometimes, but it does make debugging easier...
Tradeoffs, I guess.
Class creation the very expensive in Python, so Guido van Rossum explicitly recommends against using dynamically created namedtuples: https://mail.python.org/pipermail//python-ideas/2016-April/0...
I might be wrong though (remember seeing that talk by Heroku about how objects are treated differently even in CPython).
The better design binds the values to the string, but does not interpolate into a string type fully. Then, the resulting object can be passed to a consumer like an SQL engine (and appropriately escaped) or an internationalization library (and swapped out for a translated string before interpolation).
I mean, I appreciate what they did, but it seems like missing a massive opportunity to provide a linguistic building block for library authors.
[1] https://docs.python.org/2/library/gettext.html#gnu-gettext-a...
_('your template here {foo}').format(foo=foo)
which is a step down from f'your template here {foo}'Thank god.
I still suspect that was put in to screw over PyPy. If the type information was enforced, PyPy could use it to generate hard-typed code and improve performance. CPython, on the other hand, still represents everything as CObject objects in C. Type checking adds run-time overhead.
The annotations also allow the development of better IntelliSense-like tools.
additionally try using mypy-lang some. it's great.
value = decimal.Decimal("12.34567")
Why wouldn't this work? value = 12.34567In other words, it can be inferred to be any type that implements the Fractional typeclass, which includes Decimal types. Not that it is relevant here, because you basically need to have types for something like this to work, if you have proper type inference, then you don't need to assume any type, and can just interpret a literal as whatever is needed.
Even Swift, as modern as it gets, operates the same way. You are arguing against a convention that is as deep rooted as CPUs capable of floating point calculations.
To this day, there ARE no "decimal" CPU operations. There are integer and float operations.
I'm not arguing against Python's behavior. I do think that, at a minimum, there are major and very common software domains where the common optimization is more harmful than beneficial, and that it's good that some languages buck the trend. But while I don't think it should be an unquestioned tradition in language design, I don't think it's categorically wrobg, either.
> To this day, there ARE no "decimal" CPU operations
First, this isn't true, there are CPUs that have decimal (still floating point, so only exact within a subset of their full range) operations.
Second, it's irrelevant; many languages have types (including types for common literals) that don't map to a specialized class of operations in the CPU, where the language compiles operations down to aggregates of operations on some lower level type or types.
https://en.wikipedia.org/wiki/IEEE_floating_point
So, because users will typically be surprised that 2.2 + 3.1 results in 5.300000000000001, or that round(2.675, 2) gives 2.67 (instead of 2.68), best practice is to use Decimal, which will give users the results they expect:
One possible objection is that while Decimal is in the standard library, it isn't a built-in type, so either Decimals need to be elevated to built-ins, or some magic needs to happen when importing the decimal module to add the literal.
A similar scenario in film (that hasn't caused as much trouble for me) is aspect ratio, which is the height/width of an image. Fractions and decimals are used interchangeably in conversation.
https://docs.python.org/3/library/fractions.html
Also possibly useful in that context (and bringing us back full circle to the OP's subject), Python 3.6 has added the as_integer_ratio() method to Decimal instances:
https://docs.python.org/3.6/library/decimal.html#decimal.Dec...
D = decimal.Decimal
x = D("1.2345")
Also (at least this was once true) by doing so you save Python from a dictionary lookup after the dot "." each time.By importing a function and assigning a 1 char alias to it, the readability of the code is impacted.
"import ... as" is both common and per Google's style guide a good practice to follow in this scenario.
Going by the guide, you would do 'import pprint; pprint.pprint("foo")'...which, at least in my experience, I've never seen. It's always "from pprint import pprint" or "from pprint import pprint as pp" It could be that I work with slobs.
Similarly, I often see "from pprint import pprint as pp"
There could be an argument for switching to decimals as standard, but even ignoring the practical consequences, I think for historical reasons (I assume this is the standard of programming languages; in Ruby for example is the same) it's not something that will be ever agreed/changed.
However, for me, every time I see one of these Python 3 releases it makes me appreciate Go's model of language maintenance and development more and more.
Compare the last few major Go releases to the last few Py3 releases. Go has so much more discipline about when it's appropriate to add things to the language. Most of Go's changes are confined to bug fixes, performance improvements, and toolchain improvements.
By comparison, it seems like Python is on a runaway train of core-language additions.
> PEP 515 adds the ability to use underscores in numeric literals for improved readability. For example:
>>>
>>> 1_000_000_000_000_000
1000000000000000
A small touch but this is quite nice. primes: List[int] = []
captain: str # Note: no initial value!
class Starship:
stats: Dict[str, int] = {}If you want to fix that, you fix that first, then apply it to a subsequent patch like this one.
Additionally, I think Rust community, since its inception, embraced the goal of paying any up-front costs required for introspection.
Meanwhile, the Python community seems to be tearing between those who want syntactical beauty and one-way-to-do-it, and those who are running big production systems using IDEs like PyCharm and wanting to benefit them.
I'd prefer it if Python somehow warned people developing gigantic systems that maybe it's time to use a different language, instead of deforming Python to suit the people already running gigantic systems in a language unsuited to it.
Well, I'm not going to stop using Python or anything. I just think they're bad and ugly and I'm sad that more and more I'll open the source code of some library I'm using and find those "hints" littered everywhere.
I would plea that people reconsider their inclusion, but it seems like that ship set sail.
I prefer to use the phrase "less insecure" than "more secure".
I've been doing a lot of Go lately and hugely appreciate the minimal syntax and orthogonal features. Seems like the opposite is happening not just in Python but many languages.
Lately I've been so frustrated I've actually been researching new languages with better performance, because that's a MUCH easy sales pitch to the business managers.
Go and Python occupy different niches, for the most part. So the design considerations for Go are naturally different than those for Python.
Python 2: Stability
Python 3: Features -- Unicode, yield from, better HTTP, asyncio, more flexible destructuring, better concurrency libs, better SSL/TLS
Python 2 is still a good language because they backported a lot of the improvements that landed in Python 3. It's headed in a good direction.