Python заметки

  • Python.org – основной сайт. Тут можно скачать актуальный python на windows/linux/mac os, посмотреть документацию
  • Гвидо ван Россум – творец языка. По сути аналог Линус Торвальдса для Linux.
  • Python – the second best language for everything. И это действительно так, включая новые-модные devops, AI, BigData, autotests.
  • PEP8 – правила по написанию кода, важно соблюдать. Поможет одноименный чекер
  • У Python одна из лучших документаций в мире. 
  • Название Python не от змеи, а от комедийного шоу. 
  • Python3 по факту python 3000. В этой версии значительно улучшена архитектура. На сегодняшний день все основные библиотеки поддерживают 3’ю версию.
  • В отличии от многих других языков отступ (identation) в python очень важен и неправильный отступ может приводить к ошибкам

 

Обучение

 

Версии

  • CPython – эталонная (классическая) реализация языка. Интерпретатор написан на C.
  • Jython – специальная версия Python (судя по дате последнего апдейта в 2015 – заброшенная) для виртуальной машины Java позволяет интерпретатору выполняться на любой системе, поддерживающей Java, при этом классы Java могут непосредственно использоваться из Python и даже быть написанными на Python.
  • MicroPython – lightweight реализация для микроконтроллеров с малым потреблением памяти, процессора и питания.  Пример девайса есть на канале Dan Bader (известный популяризатор Python). 
it is compact enough to fit and run within just 256k of code space and 16k of RAM

 

Frameworks

Фреймворки:

  • Flask для простых проектов
  • Django для всего остального

 

Install

CentOS 7

Установка python3.6 (поддерживает String Interpolation) на Ubuntu 14.04.

sudo yum install https://centos7.iuscommunity.org/ius-release.rpm
sudo yum install python36
python3.6 -V
sudo yum install python36u-pip # ставим pip и setuptools
sudo pip3.6 install --upgrade pip # обновляем его
sudo pip3.6 install virtualenv
sudo pip3.6 install requests # ставим пакет requests
sudo pip3.6 install beautifulsoup4

CentOS 6

Установка python3.6 на CentOS 6.

sudo yum install https://centos6.iuscommunity.org/ius-release.rpm
sudo yum install python36u
python3.6 -V
== other same as CentOS 7 ==

Ubuntu 14.04

Установка python3.6 на Ubuntu 14.04.

sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt-get update
sudo apt-get install python3.6
sudo apt-get install python3-pip # ставим pip и setuptools
== other same as CentOS 7 ==

После установки в коде (#!/usr/bin/env python3.6) или при запуске (python3.6 <script>) указываем конкретную версию. Так же можно сделать эту версию версией по умолчанию для python3 (не рекомендую т.к. на него полагается shell – стоит ввести фигню в консоли чтобы это увидеть) или даже для “просто” python (еще меньше рекомендую) используя утилиту update-alternatives.

~$ python3 -V
Python 3.4.3
~$ sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 1
update-alternatives: using /usr/bin/python3.6 to provide /usr/bin/python3 (python3) in auto mode
~$ sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.4 2
update-alternatives: using /usr/bin/python3.4 to provide /usr/bin/python3 (python3) in auto mode
~$ sudo update-alternatives --config python3
There are 2 choices for the alternative python3 (providing /usr/bin/python3).
Selection Path Priority Status
------------------------------------------------------------
* 0 /usr/bin/python3.4 2 auto mode
1 /usr/bin/python3.4 2 manual mode
2 /usr/bin/python3.6 1 manual mode
Press enter to keep the current choice[*], or type selection number: 2
update-alternatives: using /usr/bin/python3.6 to provide /usr/bin/python3 (python3) in manual mode
~$ python3 -V
Python 3.6.8
PIP (установка и использование)

Установка модулей делается через установку из источника, easy_install или pip. Лучшим вариантом насколько понимаю является pip – это аналог gem в Ruby. Он требует отдельную установку себя like “sudo pip3.6 install requests”.

Установка библиотеки.

sudo pip3 install requests # ставим пакет requests
sudo pip3 install beautifulsoup4

Обновление библиотеки.

sudo pip3 install -U requests

Просмотр установленных библиотек.

pip3 list
Package Version
---------- ----------
certifi 2018.11.29
chardet 3.0.4
idna 2.8
pip 18.1
requests 2.21.0
setuptools 39.0.1
urllib3 1.24.1

Ошибки

Если pip не получается поставить pip с ошибкой “No package python-pip available. Error: Nothing to do”, то нужно поставить репу ius.

sudo yum install https://centos7.iuscommunity.org/ius-release.rpm

Если в качестве результата при установке пакета выдает “Consider using the `–user` option or check the permissions.”, значит нужно запускать из под sudo.

sudo pip3.6 install requests 

Если выдает “sudo: pip3.6: command not found”, то надо каждый раз при использовании pip указывать полный путь или сделать symbolic link.

sudo /usr/local/bin/pip3.6 install requests 

Если выдает timeout, то нужно проверить сеть (iptables, proxy, etc).

  Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ConnectTimeoutError(<pip._vendor.urllib3.connection.VerifiedHTTPSConnection object at 0x7f443823edd8>, 'Connection to pypi.org timed out. (connect timeout=15)')': /simple/pytest/

 

Интерпретатор

Просмотр версии, запуск в интерактивном режиме. Другие опции можно посмотреть в man python. 

$ python -V # версия второго
Python 2.7.10

$ python3 -V # версия третьего
Python 3.7.2

$ python3 # запуск в интерактивном режиме
Python 3.7.2 (v3.7.2:9a3ffc0492, Dec 24 2018, 02:44:43)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

#Простейшая математика в интерактивном режиме
>>> x=6
>>> print(x)
6
>>> x=x*6
>>> print(x)
36
quit() # выход

 

Reserved words

33 штуки

and       del       from      None      True
as        elif      global    nonlocal  try
assert    else      if        not       while
break     except    import    or        with
class     False     in        pass      yield
continue  finally   is        raise
def       for       lambda    return

 

Подключение/импорт библиотек/модулей/функций

Подключаем системную библиотеку json и из файла net_query.py (в директории исполнения текущего скрипта) подключаем только функцию model.

import json
from net_query import model

Import читает файл с модулем и создает объект этого модуля (см. пример – math).

import statement - A statement that reads a module file and creates a module object.
module object - A value created by an import statement that provides access to the data and code defined in a module.

 

Methods
Best practices
  • Использовать Logging function вместо print
  • Использовать Str.format вместо конкатенации
 
Functions
Функции есть встроенные – print(), type(), float()
Функции можно (разумеется) создавать самому. Заголовок называют header, тело – body. Функция должна быть определена перед ее вызовом, иначе будет ошибка NameError: name ‘sdfdsf’ is not defined. Результат функции генерируется через return, иначе в качестве результата будет None.
# The first line of the function definition is called the header; the rest is called the body. The header has to end with a colon and the body has to be indented. The body can contain any number of statements. As you might expect, you have to create a function before you can execute it.

def fctn():
    print("lol")
 
 
Types
  • Type(var) # узнаем тип переменной
~$ python3
>>> type("Hello weril!")
<class 'str'>
>>> type(666)
<class 'int'>
>>> type(666.6)
<class 'float'>
>>> type("666.6")
<class 'str'>
>>> type(True)
<class 'bool'>
>>> type(Funct)
<class 'function'>
>>> type(None)
<class 'NoneType'>
  • str(var) # преобразование в string
  • int(var) # преобразование в integer
  • float(var) # преобразование в float (число с плавающей ТОЧКОЙ, не запятой)
    • -str преобразование строки в int, строка должна состоять из цифр
    • -int числа в int. особо не нужно тк при операциях инт с флоат происходит автоматическая трансляция инта во флот, да даже при делении двух инт реззультатом является флоат – в Python3 (не Python2, там он отбрасывает все после точки)
 
User Input
input() # пауза и чтение пользовательского ввода, результат в виде string
var = input("what are you? ") # raw_input Python2
print ("Welcome", var)

 

File

read

def parse_file_to_list(fname):
list = open(fname).read().rstrip().split("\n")
return(list)
#берем login из файла, удаляем из него мусор (\n), кодируем в байт-строку
lgn = open('/home/redkin_p/.duser').read().rstrip().encode('ascii')
#берем большой файл, кладем его в массив (каждая строка как элемент]), ищем в каждом элементе определенный паттерн, выдергиваем из подпавших строк последний столбец (последний элемент каждого массива) и помещаем его в новый массив
data_list = open('/home/redkin_p/ORANGE').read().rstrip().split("\n")
data_list = list(x for x in data_list if "DGS-3620" in x)
ip_list = []
for s in data_list:
ip_list.append(s.split("\t")[-1].replace(" ", ""))
print(ip_list)

write

# string
def write_to_file(text, fname):
with open(fname, 'a') as the_file:
the_file.write(text)

write_to_file(text, fname)

# array
with open('/home/redkin_p/result.csv', 'a') as the_file:
for n,e in enumerate(interfaces):
res = '{};{};{};{}\n'.format(e, broadcast[n], multicast[n], unknowncast[n])
the_file.write(res)

delete

import os

os.remove("/home/redkin_p/result.csv")

 

Directory (директории)

Смотрим файлы в директории

import os

for fname in os.listdir( sys.argv[1] ):
print("{}".format(fname))

 

Date

Datetime

import datetime
print(datetime.datetime.now())

Date-time to string

datetime.datetime.now().strftime("%Y-%m-%d_%H%M%S") # for file_names
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") # for general usage

Today

import datetime
print(datetime.date.today())

 

Math

Отходящую от базовой калькуляции можно делать со стандартной библиотекой math. Далее, по аналогии с re (regexp), можно вызывая модуль объект math применять функции (или извлекать данные, например значение числа pi), определенные в этом модуль-объекте через точку (dot-notation).

import math

math.pi
math.sin(radians)
math.log10(ratio)
math.sqrt(2)

The module object contains the functions and variables defined in the module. To access one of the functions, you have to specify the name of the module and the name of the function, separated by a dot (also known as a period). This format is called dot notation.
dot notation - The syntax for calling a function in another module by specifying the module name followed by a dot (period) and the function name.

 

Комментарии

В целом о комментариях и их использовании тут.

Simple comment:

# comment

 

Random

Библиотека random позволяет генерировать псевдо-случайные числа.

The random module provides functions that generate pseudorandom numbers (which I will simply call "random" from here on).

Функция .random к объекту random генерирует случайное число от 0.1 до 1.

The function random returns a random float between 0.0 and 1.0 (including 0.0 but not 1.0).

import random

random.random()

Функция .randint к объекту random генерирует случайное int число от заданного x до y.

The function randint takes the parameters low and high, and returns an integer between low and high (including both).

random.randint(3, 10)

Функция .choice к объекту random выбирает случайный элемент из массива.

To choose an element from a sequence at random, you can use choice

random.choice([1, 4, 2, 5])

 

Кодировка (Coding)

Всегда лучше указывать кодировку в header. Может помочь от ошибок типа “Non-ASCII character ‘\xc2’ in file”.

# -*- coding: utf-8 -*-

 

 

Telnet

При использовании telnetlib с python 3+ обязательно кодируем в байт строку данные иначе будет выдавать ошибку TypeError: ‘str’ does not support the buffer interface (python 3.4) или TypeError: a bytes-like object is required, not ‘str’ (python 3.6). Это можно сделать поставив символ “b” перед строкой или применив к нему метод encode (с кодировкой ascii или без указания кодировки). Полученные данные в str декодируем в байт строку через decode.

#!/usr/bin/env python3.6
#coding: utf-8

import telnetlib

lgn = open('/home/redkin_p/.duser').read().rstrip().encode() #берем login из файла, удаляем из него мусор (\n), кодируем в байт-строку
pswd = open('/home/redkin_p/.dpass').read().rstrip().encode() #берем password из файла, удаляем из него мусор (\n), кодируем в байт-строку

tn = telnetlib.Telnet("10.10.10.10") #подключаемся к узлу
tn.read_until("UserName:".encode('ascii')) #ждем строку "UserName:"
tn.write(lgn + b"\r") #передаем кодированный в байт-строку login
tn.read_until(b"PassWord:") #ждем строку "PassWord:"
tn.write(pswd + b"\r") #передаем кодированный в байт-строку password
tn.read_until(b"#") #ждем строку "#"
tn.write(b"show switch\r") #передаем необходимый запрос
res = tn.read_until(b"#") #забираем данные
tn.close

print(res.decode('ascii')) #

 

Conditional structures

If/else/elif. В случае использования elif не обязателен else (в отличии от Ruby), а подпадание под несколько elif не означает исполнения кода под каждым – только под первым (как ACL в сетевом мире). 

# one-line
if res == "test": print(x)
if "string" in somevar: print("Hit")

# simple
if res == "test":
  print("true")

# if with else
if res == "test":
print("true")
else:
print("false")

# if with else with elif
if res == "test":
  print("true")
elif res == "test2":
pass # do nothing
elif res == "test3":
print("false3")
else:
print("false")

# можно делать конструкции и без else (если не подпадет ни под одно из условий, то не отработает никак)
if res == "test":
  print("true")
elif res == "test2":
print("false")

 

Comment

# обычный однострочный
''' или """ multiline

 

String (или байт строка)

Вывод

print (x) # вывод переменной х
print (‘hello world’) # вывод текста hello world (можно как в двойных, так и в одинарных скобках)

Замена всех символов (по умолчанию), которые подпадают под первое условие, с регулярными выражениями не работает:

.replace("\x08", "")

В третьем аргументе можно указать количество подпадений, которое нужно поменять (чаще всего нужно первое):

.replace("\x08", "", 1)

Ищем строку в переменной:

if "string" in somevar: print("Hit")

Простая конкатенация:

print ("Hello"+name+"master") # конкатенация статичного текста и переменных без пробела
print ("Hello", name, "master") # конкатенация статичного текста и переменных через пробел

Конкатенация через .format, довольно удобно, хотя и хуже ruby string interpolation

res = '{};{};{};{}\n'.format(e, broadcast[n], multicast[n], unknowncast[n])

Можно еще так же множить текст, умножив str на int

>>> first = 'Test '
>>> second = 3
>>> print(first * second)
Test Test Test

Downcase/lowercase, upcase/uppercase

.lower()
.upper()

Многострочная переменная (multiline variable):

var = '''line1
line2
line3'''
FIND

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

s = "hello.my@world.me"
pos = s.find("@")
print(pos)
pos2 = s.find(".", pos) # второй аргумент в find определяет стартовую позицию, иначе в выборку попал бы индекс 5 вместо 14
print(pos2)
res = s[pos+1:pos2]
print(res)

# test.py
8
14
world
REGEXP

Замена символов с поддержкой regexp, где x перменная со строкой:

import re

re.sub(r".*interface gei-0/1/1/", '', x)

Вместо регулярных выражений зачастую можно использовать split, что будет работать быстрее/занимать меньше строк кода:

  • для ^ или $ использовать split(” “)[0] с доступом к первому или последнему элементу
x.split(" ")[0]
  • для того же, после определенного pattern можно задавать split с этом pattern (например, для получения числа, окруженного двумя уникальными для строки pattern – blabsdfsdfbla;user_id:666;user_class:admin;blablalblsdfsdf)
x.split(";user_id:")[1].split(";user_class:")[0]

Case insensitive

res = re.sub(r".*(cve[,_-]\d\d\d\d[,_-]\d\d\d\d?\d)", '', x, flags=re.IGNORECASE)
Array/LIST

Длина массива или строки (length)

len(arr)

Максимальный/минимальный элемент (работает для элементов в виде str, так и для int; работает не только для array, но и просто для строки)

max(arr)
min(arr)
Удаление

Разность массивов (находим значения, которые уникальны в первом массиве относительно второго):

def remove(a, b):
    return list(set(a) - set(b))
Пересечение

Пересечение массивов (находим значения, которые есть в обоих массивах):

def intersect(a, b):
    return(list(set(a) & set(b)))
LOOP (WHILE TRUE)
while True:
...
Split
arr.split("\n")
Join
delim = "%"
arg_str = delim.join(sys.argv[1:])
Sleep
import time

time.sleep(1)
Slice

Получаем первые пять элементов массива (или букв строки) и кучу разных других ништяков

arr[beginning_of_string:end_of_string]
arr[:5] #первые пять элементов массива
arr[0:5] #первые пять элементов массива
s[-3:] #последние 3 элемента
s[:] #копия списка, часто очень полезно
s[1:] #все элементы кроме первого
s[2:-2] #откидываем первые и последние 2
s[::2] #парные элементы
s[1:4:2] #элементы с первого по четвертый с шагом 2
Iteration/Итерация

Без индекса

for sw in sw_list:
print(sw)

С индексом

for n,e in enumerate(interfaces):
print(n)
for key, value in d.items():

Range

for i in range(5):
print(i)

Добавление элемента

# Одного
sw_list_with_hostname = []
for sw in sw_list:
sw_list_with_hostname.append(sw)

# Нескольких (через вложенный массив)
sw_list_with_hostname = []
for sw in sw_list:
sw_list_with_hostname.append([sw, hostname])
Поиск элементов

Самый простой способ:

7 in a # 7 in array?

Пример

#берем большой файл, кладем его в массив (каждая строка как элемент]), ищем в каждом элементе определенный паттерн, выдергиваем из подпавших строк последний столбец (последний элемент каждого массива) и помещаем его в новый массив
data_list = open('/home/redkin_p/ORANGE').read().rstrip().split("\n")
data_list = list(x for x in data_list if "DGS-3620" in x)
ip_list = []
for s in data_list:
ip_list.append(s.split("\t")[-1].replace(" ", ""))
print(ip_list)

collections

Аналог sort | uniq –c (количество уникальных объектов)

from collections import Counter
input = ['a', 'a', 'b', 'b', 'b']
c = Counter( input )
sort

Сортировка,

flist.sort()

через равно делать не надо, будет ошибка

flist = flist.sort()

TypeError: 'NoneType' object is not iterable
array of unique values

Один массив

list(set(array))

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

def union(a, b):
return list(set(a) | set(b))
Dictionary

Python dictionary (hash, associative array)

a = {"id": 1, "name":"Sesame Street"}
Thread

Многопоточность можно запустить очень просто

from multiprocessing.dummy import Pool as ThreadPool 

def query_data(ip):
print(ip)

pool = ThreadPool(10) # количество потоков
pool.map(query_data, sw_list) # запускаем функцию query_data и передаем ей на входе массив sw_list

Если аргументов в функцию нужно передать несколько, можно создать двумерный массив и далее:

1) передать его starmap вместо одномерного и наслаждаться стандартной функцией

from multiprocessing.dummy import Pool as ThreadPool 

def query_data(ip, hostname):
print(ip)

pool = ThreadPool(10) # количество потоков
sw_list_with_hostname = [] # создаем новый массив вида [[ip, hostname], [ip2, hostname]]
for sw in sw_list:
sw_list_with_hostname.append([sw, hostname])
pool.starmapmap(query_data, sw_list_with_hostname) # запускаем функцию query_data и передаем ей на входе массив sw_list_with_hostname

2) передать его pool.map вместо одномерного и разбирать на входе функции (менее удобно)

from multiprocessing.dummy import Pool as ThreadPool 

def query_data(data):
ip = data[0]
hostname = data[1]
print(ip)

pool = ThreadPool(10) # количество потоков
sw_list_with_hostname = [] # создаем новый массив вида [[ip, hostname], [ip2, hostname]]
for sw in sw_list:
sw_list_with_hostname.append([sw, hostname])
pool.map(query_data, sw_list_with_hostname) # запускаем функцию query_data и передаем ей на входе массив sw_list_with_hostname
ARGV

Получаем данные из системных переменных в виде array – путь к исполняемому файлу (всегда в argv[0]) и далее опционально (что передали) – имена файлов, адреса (IP, mail, url), все что угодно.

import sys

print(sys.argv)
print(sys.argv[1])
quit()
Exception

Отработка на все ошибки, без сбора информации о ошибке

try:
print("here is untrusted code")
except: # catch *all* exceptions
print("exception")

Отработка на все ошибки, со сбором информации о ошибке

try:
print("here is untrusted code")
except Exception as e:
print("exception")
OS.SYSTEM (EXTERNAL COMMAND)

С системным модулем subprocess запуск внешних команд похож на обычный shell или запуск в ruby/php через ‘command’. НО есть огромная разница – вывод не сохранить сразу в переменную в python, он только отображается в bash.

import sys

os.system("ip netns exec VRF1 ip a | grep 'inet ' | awk '{print $NF}'")
Subprocess (external command)

С системным модулем subprocess запуск внешних команд похож gem Ruby Open3.

import subprocess

subprocess.run(["ls", "-l"])
subprocess.check_output(["snmpget", "-v", "2c", "-c", "test", "-Ov", "-Oq", ip, oid])

Для длинных команд удобно сначала использовать split и переменную.

cmd = "ip netns exec VRF1 ip a".split(" ")
subprocess.run(cmd)

Вывод STDOUT/STDERR скрыть можно через опции.

import os
import subprocess

null = open(os.devnull, 'w')
subprocess.run(cmd, stderr=null, stdout=null)

Получение stdout в subprocess.

test = subprocess.Popen(cmd, stdout=subprocess.PIPE)
data = test.communicate()
print(data)

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

Запуск background subprocess.

test = subprocess.Popen(cmd)

Запуск в background + запись в файл. Реализация через shell:

  • tee для записи в файл,
  • & для background,
  • stdout=null для отброса вывода в консоль (запись только в файл)
import os
null = open(os.devnull, 'w')

subprocess.call('some_script.hz | tee log_file &', shell=True, stdout=null)

 

JSON

Python содержит встроенный модуль под названием json для кодирования и декодирования данных JSON.

import json

arr = { "test": 2, "test2": "xxx", "arr": [1,2,7] }
print(arr)
encoded = json.dumps(arr)
print(encoded)
decoded = json.loads(encoded)
print(decoded)

>
{'test': 2, 'test2': 'xxx', 'arr': [1, 2, 7]}
{"test": 2, "test2": "xxx", "arr": [1, 2, 7]}
{'test': 2, 'test2': 'xxx', 'arr': [1, 2, 7]}

 

Parsing WEB/парсинг

Простейший get-аналог curl с входом из терминала в виде трех переменных и выводом в терминал.

import requests
import sys

one = sys.argv[1]
two = sys.argv[2]
three = sys.argv[3]

# Set the URL you want to webscrape from
url = 'http://127.0.0.1:2222/?text={}%{}%{}'.format(one, two, three)
# Connect to the URL
print(requests.get(url).text)

Можно оптимизировать используя join c разделителем % и отбросом через slice первого элемента, чтобы на входе были любое количество переменных.

delim = "%"
arg_str = delim.join(sys.argv[1:])

# Set the URL you want to webscrape from
url = 'http://127.0.0.1:2222/?text={}'.format(arg_str)

С utf-8 есть траблы. Вот как решаются:

# Узнать кодировку ответа
requests.get(url).encoding

# Использовать заданную кодировку на полученный ответ
requests.encoding = 'utf-8'

# Пример
res = requests.get(url)
print(res.encoding)
res.encoding = 'utf-8'
print(res.text)
Ускорение работы
  • Потоки python по умолчанию выполняются на одном ядре
  • Для ускорения python можно почитать эту статью
ошибки

Выдает если разные разделители (tab and spaces), нужно к одному привести (лучше в соответствии с pep-8 4 spaces вместо каждого tab).

TabError: inconsistent use of tabs and spaces in indentation

Меняем byte object на string

strng.decode()
#a bytes-like object is required, not 'str'

Меняем string на byte object

btobj.encode()
#cannot use a string pattern on a bytes-like object

Leave a Reply