Архив рубрики: Python

Копирование объектов в Python

В прошлый раз я рассмотрел, как работает присваивание в Python. Как выяснилось, присваивание всегда создает новую ссылку на присваиваемый объект и связывает эту ссылку с именем слева от знака присваивания. Аналогично, при передаче значения в вызываемую функцию, с именем параметра связывается новая ссылка на передаваемый объект. Ни при присваивании, ни при передаче аргументов не происходит копирования объектов.

Как же получить копию объекта в Python?

Ответ на этот вопрос, с короткими примерами на Python, читайте в блокноте, подготовленном с помощью jupyter-notebook и опубликованном на github, который умеет показывать такие блокноты.

Автор: Andrei Trofimov

PyPDF2: новый форк pyPdf (Перевод)

Сегодня узнал, проект pyPDF НЕ умер, как я думал раньше. На самом деле он перешёл в PyPDF2 (видите небольшую разницу в написании?). Есть так же вероятность того, что кто-то занимается и оригинальной версией проекта. При желании, за судьбой PyPDF2 можно следить на reddit. Тем временем я решил посмотреть, чем же PyPDF2 отличается от оригинала. Так что если у Вас есть пара свободных минут — милости просим.

Введение в PyPDF2

Два года назад я уже  писал про pyPDF, а совсем недавно мне как раз пришлось погрузиться в различные библиотеки для работы с PDF на Python, так что появление нового форка пришлось кстати. Мы возьмём несколько моих старых примеров, запустим их в PyPDF2 и посмотрим, как они сработают.
# Соединяем два PDF
from PyPDF2 import PdfFileReader, PdfFileWriter
 
output = PdfFileWriter
()
pdfOne = PdfFileReader
(open( "somepathtoaPDf", "rb"))
pdfTwo = PdfFileReader
(open("someotherpathtoaPDf", "rb"))
 
output.
addPage(pdfOne.getPage(0))
output.
addPage(pdfTwo.getPage(0))
 
outputStream =
open(r"output.pdf", "wb")
output.
write(outputStream)
outputStream.
close()
На моём Windows 7 это сработало без проблем. Как Вы можете предположить, всё, что этот код делает — создаёт два объекта PdfFileReader и прочитывает в каждом первую страницу. После этого, он добавляет эти две страницы в наш PdfFileWriter. И, наконец, мы открываем новый файл и записываем в него PDF. Вот и всё! Мы только что создали новый документ из двух разных PDF!
Теперь давайте попробуем скрипт для поворота страницы из другой моей страницы:
from PyPDF2 import PdfFileWriter, PdfFileReader
 
output = PdfFileWriter()
input1 = PdfFileReader(file("document1.pdf", "rb"))
output.addPage(input1.getPage(1).rotateClockwise(90))
# output.addPage(input1.getPage(2).rotateCounterClockwise(90))
 
outputStream = file("output.pdf", "wb")
output.write(outputStream)
outputStream.close()
Этот скрипт так же сработал на моей машине. Очень даже хорошо. Мой последний тест на соответствие должен проверить, можем ли мы вытаскивать данные так же, как мог оригинальный pyPdf. Давайте попробуем прочитать метаданные:
>>> from PyPDF2 import PdfFileReader
 
>>> p = r'C:UsersmdriscollDocumentsreportlab-userguide.pdf'
 
>>> pdf = PdfFileReader(open(p, 'rb'))
 
>>> pdf.documentInfo
 
{'/ModDate': u'D:20120629155504', '/CreationDate': u'D:20120629155504', '/Producer': u'GPL Ghostscript 8.15', '/Title': u'reportlab-userguide.pdf', '/Creator': u'Adobe Acrobat 10.1.3', '/Author': u'mdriscoll'}
>>> pdf.getNumPages()
 
120
>>> info = pdf.getDocumentInfo()
 
>>> info.author
 
u'mdriscoll'
>>> info.creator
 
u'Adobe Acrobat 10.1.3'
>>> info.producer
 
u'GPL Ghostscript 8.15'
>>> info.title
 
u'reportlab-userguide.pdf'
Тоже очень хорошо, кроме поля автора. Я определённо не автор этого документа и я не знаю, почему PyPDF2  решил, что я им являюсь. Всё остальное сработало верно. Теперь давайте посмотрим, что тут нового.

Что нового в PyPDF2

Одна из вещей, которую я сразу заметил как только взглянул на исходники PyPDF2, это то, что он добавляет несколько новых методов в PdfFileReader и PdfFileWriter. Кроме того, я заметил, что появился совершенно новый модуль merger.py, который содержит класс PdfFileMerger. Поскольку никакой нормальной документации нет — придётся лезть «под капот». Единственный новый метод, который был добавлен в reader — это getOutlines, который позволяет получить эскизы документа (outlines), если они есть. В writer появилась возможность добавить bookmarks и named destinations. Не так уж и много, но дарёному коню в зубы не смотрят. Но больше всего меня вдохновил новый класс PdfFileMerger, который мне напомнил почти погибший проект Stapler. PdfFileMerger позволяет нам объединить несколько PDF в один, используя соединение, вставку и вырезку и их комбинации.
Давайте попробуем?
import PyPDF2
 
path = open('path/to/hello.pdf', 'rb')
path2 = open('path/to/another.pdf', 'rb')
 
merger = PyPDF2.PdfFileMerger()
 
merger.merge(position=0, fileobj=path2)
merger.merge(position=2, fileobj=path)
merger.write(open("test_out.pdf", 'wb'))
Тут мы соединяем два файла вместе. В первый файл вместо третьей страницы будет добавлен второй файл, а как только содержание второго файла закончится будет продолжен первый файл. Это проще, чем проходиться по страницам двух документов и соединять их. Команда merge имеет следующую сигнатуру и строку документации, которая хорошо всё это описывает:
>>> merge(position, file, bookmark=None, pages=None, import_bookmarks=True)
 
Сливает страницы из исходного документа, определённого в
"file" в файл вывода на место, определённое в "position".
 
Опционально Вы можете определить закладку, которая будет установлена на начало этого файла. Для этого надо передать текст закладки в параметре
"bookmark".
 
Вы можете запретить импорт закладок из исходного документа если передадите значение False параметру
"import_bookmarks".
 
Вы можете так же использовать параметр "pages" для того, чтобы добавить только некоторые страницы исходного файла. Для этого надо задать их диапазон.
Есть так же метод append, который аналогичен команде merge, но вместо того, чтобы сливать файлы, он добавляет их в конец исходного файла. Вот скрипт для примера:
import PyPDF2
 
path = open('path/to/hello.pdf', 'rb')
path2 = open('path/to/another.pdf', 'rb')
 
merger = PyPDF2.PdfFileMerger()
 
merger.append(fileobj=path2)
merger.append(fileobj=path)
merger.write(open("test_out2.pdf", 'wb'))
И совсем не больно!

Подводим итоги

Мне кажется, это хорошая альтернатива для работы с PDF. Я могу комбинировать и разделять PDF при помощи PyPDF2 даже проще, чем с оригинальным pyPdf. Кроме того, я надеюсь, что PyPDF не умрёт, так как у него есть спонсоры, оплачивающие его разработку. Согласно потокам reddit есть даже шанс, что оригинальный pyPdf будет переработан и в конце концов оба эти проекта сольются в дружеском экстазе. Но, вне зависимости от того, чем всё закончится, я рад, что разработка его возобновилась и надеюсь, что она не скоро прекратится.

Домашнее чтение

Автор: Ishayahu Lastov

Присваивание в Python

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

Переменную в C часто сравнивают с коробкой определенной формы и размера, на которой написано имя переменной, а внутри лежит значение. Размер и форма коробки — ее тип — диктует, какие значения можно положить в эту именованную коробку, то есть, присвоить переменной с данным именем. Ничего подобного нет в Python. Переменная в Python — это просто именованный указатель на…

Если в C одной переменной типа int присвоить значение другой переменной того же типа, то мы получим два одинаковых и независимых друг от друга значения, каждое из которых будет занимать свое место в памяти (то есть, лежать в своей отдельной коробке, на которой написано имя переменной). Не так в Python, где все значения являются объектами…

В таких языках как C передача параметров в функцию бывает по значению или по ссылке. В первом случае параметру функции присваивается передаваемое значение, а во втором — ссылка на значение (указатель). Тогда из функции можно изменить объект, на который указывает ссылка. Но в Python есть только один способ передачи параметров в функцию…

Продолжение истории, с короткими примерами на Python, читайте в блокноте, подготовленном с помощью jupyter-notebook и опубликованном на github, который умеет показывать такие блокноты.

Автор: Andrei Trofimov

Пространства имен в Python

Namespaces are one honking great idea — let's do more of those!

The Zen of Python, by Tim Peters

Как известно, в Python есть локальные пространства имен (внутри функции или класса, которые могут быть вложенными), глобальное (на уровне текущего модуля) и встроенное пространство имен модуля builtins. К последнему Python обращается в последнюю очередь, если искомое имя не находится в локальном и глобальном пространствах имен.

Посмотрим на пространства имен Python в интерактивном режиме.

C:_dev> python.exe
Python 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> __name__
'__main__'

Это имя текущего модуля, в глобальном пространстве имен которого мы сейчас находимся. А функция dir() без аргументов покажет нам и другие имеющиеся в нем имена:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']

Попробуем увидеть, что за ними стоит. Нам помогут цикл for и eval():

>>> for name in dir(): print(name, eval(name), eval('type('+name+')'))
...
__annotations__ {}
__builtins__
__doc__ None
__loader__
__name__ __main__
__package__ None
__spec__ None

Приблизительно такую же информацию можно получить при помощи встроенной функции loclas(), которая возвращает словарь с ключами — именами текущего пространства имен:

>>> locals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__':
, '__spec__': None, '__annotations__': {}, '__builtins__': , 'name': '__spec__'}

(Заметьте, в пространстве имен появилось имя name, использованное нами выше в качестве переменной в цикле for. И с именем name связано значение, присвоенное ему в цикле последним.)

Наше текущее пространство имен при вводе команд в интерактивном режиме Python — это глобальное пространство имен модуля __main__. Поэтому словарь, возвращаемый функцией globals() и представляющий глобальное пространство имен, совпадает со словарем, возвращаемым locals():


>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__':
, '__spec__': None, '__annotations__': {}, '__builtins__': , 'name': '__spec__'}

>>> locals() == globals()
True

Мы вернемся к разнице между locals() и globals() позднее. А пока посмотрим внимательно на то, что имеется в глобальном пространстве имен.

Под именем __builtins__ в глобальном пространстве имен имеется модуль builtins, который автоматически импортируется при запуске Python. Этот модуль содержит все встроенные функции и классы, в том числе такие знакомые, как int, float, list, а также все встроенные исключения (exceptions):

>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferEr
ror', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'Conne
ctionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsErr
or', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarni
ng', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'Lookup
Error', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplement
edError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionEr
ror', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'Syn
taxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalE
rror', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarni
ng', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '_', '__build_class__', '__debug__', '__doc__', '__im
port__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray',
'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divm
od', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr& #39;, 'ha
sh', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map',
'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr'
, 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'var
s', 'zip']

В нем есть имена функций dir, eval, locals и globals, которыми мы воспользовались выше так, как будто их имена известны в текущем пространстве имен. Дело в том, что имена, не найденные в глобальном пространстве имен, Python ищет в пространстве имен модуля builtins.

Следующие два вызова по сути одно и то же:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'name']
>>> __builtins__.dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'name']

Кстати, любопытно, что не все так называемые встроенные функции Python на самом деле являются функциями; часть из них — классы. Это видно из следующего фрагмента:

>>> for name in dir(__builtins__): print(name, eval(name), eval('type('+name+')'))
...
ArithmeticError
AssertionError
AttributeError
BaseException
BlockingIOError
BrokenPipeError
BufferError
BytesWarning
ChildProcessError
ConnectionAbortedError
ConnectionError
ConnectionRefusedError
ConnectionResetError
DeprecationWarning
EOFError
Ellipsis Ellipsis
EnvironmentError
Exception
False False
FileExistsError
FileNotFoundError
FloatingPointError
FutureWarning
GeneratorExit
IOError
ImportError
ImportWarning
IndentationError
IndexError
InterruptedError
IsADirectoryError
KeyError
KeyboardInterrupt
LookupError
MemoryError
ModuleNotFoundError
NameError
None None
NotADirectoryError
NotImplemented NotImplemented
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
PermissionError
ProcessLookupError
RecursionError
ReferenceError
ResourceWarning
RuntimeError
RuntimeWarning
StopAsyncIteration
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TimeoutError
True True
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
WindowsError
ZeroDivisionError
__build_class__
__debug__ True
__doc__ None
__import__
__loader__
__name__ __main__
__package__ None
__spec__ None
abs
all
any
ascii
bin
bool
bytearray
bytes
callable
chr
classmethod
compile
complex
copyright Copyright (c) 2001-2017 Python Software Foundation.
All Rights Reserved.

Copyright (c) 2000 BeOpen.com.
All Rights Reserved.

Copyright (c) 1995-2001 Corporation for National Research Initiatives.
All Rights Reserved.

Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
All Rights Reserved.
credits Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
for supporting Python development. See www.python.org for more information.
delattr
dict
dir
divmod
enumerate
eval
exec
exit Use exit() or Ctrl-Z plus Return to exit
filter
float
format
frozenset
getattr
globals
hasattr
hash
help Type help() for interactive help, or help(object) for help about object.
hex
id
input
int
isinstance
issubclass
iter
len
license Type license() to see the full license text
list
locals
map
max
memoryview
min
next
object
oct
open
ord
pow
print
property
quit Use quit() or Ctrl-Z plus Return to exit
range
repr
reversed
round
set
setattr
slice
sorted
staticmethod
str
sum
super
tuple
type
vars
zip

Так, например, eval, any и all являются builtin_function_or_method, а вот filter, map и range являются экземплярами класса type, то есть, классами. (Да ведь это итераторы!)

Следующая попытка разрешить имя pi не удается, потому что это имя не определено в глобальном пространстве имен и не определено в модуле builtins:

>>> pi
Traceback (most recent call last):
File "", line 1, in
NameError: name 'pi' is not defined

Оно определено в модуле math. Импортируем этот модуль и проверим, что изменилось в текущем пространстве имен:

>>> import math
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'math', 'name']

В текущем пространстве имен появилось имя math. Это имя указывает на модуль math:

>>> math

В котором есть много интересного:

>>> dir(math)
['__doc__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'ata
nh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fm
od', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log',
'log10', 'log1p', 'log2', 'modf', 'nan', 'pi',  9;pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']

В том числе, интересующее нас имя pi, с которым связано число пи:

>>> math.pi
3.141592653589793

Мы даже можем его переопределить (хотя это хулиганство):

>>> math.pi = 123.123
>>> math.pi
123.123

Вернемся теперь к вопросу о локальном пространстве имен. Чтобы функция locals() вернула словарь для локального пространства имен, нужно вызвать ее внутри нашей собственной функции, где текущее простраство имен — локальное пространство имен этой функции.

>>> def loc():
... x = 1
... y = 2
... print(locals() == globals())
... print(locals())
... print(globals())
...
>>> loc()
False
{'y': 2, 'x': 1}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__':
, '__spec__': None, '__annotations__': {}, '__builtins__': , 'name': '__spec__', 'math': <
module 'math' (built-in)>, 'loc': }

Теперь разница между locals() и globals() очевидна.

А в глобальном пространстве имен появилось имя loc, указывающее на определенную нами функцию. Вызовем ее по-другому:

>>> globals()['loc']()
False
{'y': 2, 'x': 1}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__':
, '__spec__': None, '__annotations__': {}, '__builtins__': , 'name': '__spec__', 'math': <
module 'math' (built-in)>, 'loc': }

Можно вызвать и так (ведь на уровне модуля глобальное и локальное пространства имен совпадают):

>>> locals()['loc']()
False
{'y': 2, 'x': 1}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__':
, '__spec__': None, '__annotations__': {}, '__builtins__': , 'name': '__spec__', 'math': <
module 'math' (built-in)>, 'loc': }

А как поживает число пи в модуле math?

>>> locals()['math'].pi
123.123

Прекратим это безобразие и, пожалуй, закончим на сегодня:

>>> quit()

C:_dev>

Автор: Andrei Trofimov
Дата публикации: 2017-11-30T15:02:00.000+10:00

Какой язык нужно учить, чтобы зарабатывать сейчас больше

Какой язык нужно учить, чтобы зарабатывать сейчас больше

Конечно же все знают, что сотрудники практически любой компании хорошо владеющие иностранными языками и ценятся начальством больше, да и заработать могут поболее своих коллег — это не секрет. В связи с этим холдинг РБК недавно заказал популярному рекрутинг-ресурсу HeadHunter.ru провести исследование о том как знание иностранных языков повышает зарплатные ожидания у соискателей и ниже для читателей будут даны наиболее важная информация по этой теме.