MySQL e Python

Para usar MySQL em Python, no Linux Slackware, que é a distribuição de Linux que eu uso, tive que instalar o módulo MySQL-python. Para Windows, Mac, ou outras distribuições de Linux, encontrei as instruções seguintes no StackOverflow:

For Windows user, you can get an exe of MySQLdb.

For Linux, this is a casual package (python-mysqldb). (You can use sudo apt-get install python-mysqldb (for debian based distros), yum install mysql-python (for rpm-based), or dnf install python-mysql (for modern fedora distro) in command line to download.)

For Mac, you can install MySQLdb using Macport.

Para o Slackware, fui ao sítio do SlackBuilds e descarreguei o script de compilação, assim como o código fonte do MySQL-python, que é descarregado do sítio do Python. O script cria um pacote instalável.

Criei, então, um script em Python, com o código seguinte, para imprimir todo o conteúdo de uma tabela de uma base de dados:

!/usr/bin/python
import MySQLdb

db = MySQLdb.connect(host="localhost",
                     user="user",
                     passwd="pass",
                     db="livros")

cur = db.cursor()
cur.execute("SELECT * FROM livro")

for row in cur.fetchall():
    row = list(map(lambda x: str(x), row))
    print ", ".join(row)
    #print type(row)

db.close()

É aconselhável não ter as credenciais de acesso à base de dados – username e password – dentro do ficheiro de Python. A melhor solução é guardar esses dados num ficheiro noutra pasta, e depois importar esse ficheiro (ver mais informação aqui) sempre que for necessário. Eis como se importa um ficheiro em Python:

#!/usr/bin/python
import imp

dba = imp.load_source('module.dba', '/tmp/db/acessobd.py')

print dba.srv, dba.usr, dba.psw

Para mais informação e exemplos sobre o uso de MySQL em Python, nomeadamente, sobre a utilização de ORMs fica aqui o link de uma entrada no StackOverflow.

O último exemplo é uma inserção numa tabela da base de dados.

#!/usr/bin/python
# -*- coding: utf-8 -*-
import MySQLdb
import imp

dba = imp.load_source('module.dba', '/tmp/db/acessobd.py')

db = MySQLdb.connect(host="localhost",
			user=dba.usr,
			passwd=dba.psw,
			db="livros")

cur = db.cursor()

try:
        cur.execute("""INSERT INTO livro VALUES (NULL,%s,%s,%s)""",('Biblia', 'Moises', -2000))
        db.commit()
except:
        db.rollback()

db.close()

Listas

Considere-se a lista seguinte, assim como algumas operações de consulta da lista.

>>> myList = ["The", "earth", "revolves", "around", "sun"]
>>> myList
['The', 'earth', 'revolves', 'around', 'sun']

>>> myList[0]
'The'

>>> myList[4]
'sun'

>>> myList[5]
Traceback (most recent call last):
  File "", line 1, in 
IndexError: list index out of range

Um índice negativo consulta a lista a partir da extremidade final

>>> myList[-1]
'sun'

Podem inserir-se elementos em posições determinadas.

>>> myList.insert(0,"Yes")
>>> myList
['Yes', 'The', 'earth', 'revolves', 'around', 'sun']

Para adicionar elementos à lista, é possível usar a função append ou a função extend. A primeira adiciona apenas um elemento. A segunda, se o elemento for uma lista, estende-a com os elementos dessa lista.

>>> myList.append(["a", "true"])

>>> myList
['Yes', 'The', 'earth', 'revolves', 'around', 'sun', ['a', 'true']]

>>> len(myList)
7
>>> myList.extend(["statement", "for", "sure"])

>>> myList
['Yes', 'The', 'earth', 'revolves', 'around', 'sun', ['a', 'true'], 'statement', 'for', 'sure']

>>> len(myList)
10

Podem extrair-se elementos de uma lista com a função slice.

>>> myList[1:4]
['The', 'earth', 'revolves']

>>> myList[:4]
['Yes', 'The', 'earth', 'revolves']

>>> myList[4:]
['around', 'sun', ['a', 'true'], 'statement', 'for', 'sure']

>>> myList[:]
['Yes', 'The', 'earth', 'revolves', 'around', 'sun', ['a', 'true'], 'statement', 'for', 'sure']

Pesquisa em listas a partir do conteúdo:

>>> myList.index("revolves")
3

>>> myList.index("a")
Traceback (most recent call last):
  File "", line 1, in 
ValueError: 'a' is not in list

>>> myList.index(["a", "true"])
6

>>> "sun" in myList
True

Remover elementos:

>>> myList
['Yes', 'The', 'earth', 'revolves', 'around', 'sun', ['a', 'true'], 'statement', 'for', 'sure']

>>> myList.remove("Yes")
>>> myList
['The', 'earth', 'revolves', 'around', 'sun', ['a', 'true'], 'statement', 'for', 'sure']

>>> myList.remove("statement")
>>> myList
['The', 'earth', 'revolves', 'around', 'sun', ['a', 'true'], 'for', 'sure']

>>> myList.remove(["a", "true"])
>>> myList
['The', 'earth', 'revolves', 'around', 'sun', 'for', 'sure']

>>> myList.pop()
'sure'
>>> myList
['The', 'earth', 'revolves', 'around', 'sun', 'for']

Operadores de listas

>>> myList
['The', 'earth', 'revolves', 'around', 'sun', 'for']
>>> myList = myList + ["sure"]
>>> myList
['The', 'earth', 'revolves', 'around', 'sun', 'for', 'sure']

>>> myList += ["."]
>>> myList
['The', 'earth', 'revolves', 'around', 'sun', 'for', 'sure', '.']

>>> myList *= 2
>>> myList
['The', 'earth', 'revolves', 'around', 'sun', 'for', 'sure', '.', 'The', 'earth', 'revolves', 'around', 'sun', 'for', 'sure', '.']

	

Formatar datas

Precisei de mudar uma data do formato americano mm/dd/aaaa, para o formato do mysql aaaa-mm-dd.

Embora isto possa ser feito de muitas outras formas, algumas eventualmente mais eficientes, aqui vai uma solução que eu imaginei, com os conhecimentos adquiridos até agora.

import re

data = "5/23/2016"
p = re.compile("(\d+)/(\d+)/(\d+)")
r = p.search(data)
print "{0}-{1:02d}-{2:02d}".format(r.group(3), int(r.group(1)), int(r.group(2)))

Ficheiros csv

O código seguinte lê um ficheiro CSV e imprime cada campo numa linha

#!/usr/bin/python
import re

with open("b.csv") as f:
        linhas = f.readlines()

regex = re.compile(r"^\xef\xbb\xbf")
linhas = list(map(lambda x: regex.sub("", x).rstrip(), linhas))

for lin in linhas:
        campos = lin.split('","')
        campos[0] = campos[0][1:]
        tam = len(campos)
        tam2 = len(campos[tam-1])
        campos[tam-1] = campos[tam-1][:tam2-1]
        for i in campos:
                print i

Tal como no artigo anterior, remove o BOM do início das linhas. Usualmente, o BOM está só na primeira linha, mas no caso de se concatenarem ficheiros, pode aparecer em várias linhas do ficheiro. Remove também o \r\n do fim de cada linha do ficheiro.

Depois, parte cada linha pelo separador de campos “,”. Por fim, remove as aspas do início do primeiro campo e do fim do último campo.

Ficheiros

Para o meu projeto, preciso de ler ficheiros em Python. A primeira ação que preciso de executar é listar os ficheiros de uma pasta. Para isso vou usar o código seguinte:

import os
os.listdir("path")   # devolve uma lista

Para listar os ficheiros com extensão “.csv”, podemos usar o código seguinte:

import os
import re
d = os.listdir(".")
for i in d[:]:
    if re.match('.*\.csv$', i): 
        print i

E agora um programa que lê um ficheiro de texto para uma lista de linhas, retira o \r\n (rstrip) do fim das linhas e o BOM do início. No fim, imprime as linhas limpas.

#!/usr/bin/python
# -*- coding: utf-8 -*-

import re

with open("a.txt") as f:
    linhas = f.readlines()

regex = re.compile(r"^\xef\xbb\xbf")
linhas = list(map(lambda x: regex.sub("", x).rstrip(), linhas))

for i in linhas:
    print i

Instruções compostas

As instruções compostas do Python podem ser as instruções seguintes, definidas usando a sintaxe BNF:

  • if
    if_stmt ::=  "if" expression ":" suite
                 ( "elif" expression ":" suite )*
                 ["else" ":" suite]
    
  • while
    while_stmt ::=  "while" expression ":" suite
                    ["else" ":" suite]
    
  • for
    for_stmt ::=  "for" target_list "in" expression_list ":" suite
                  ["else" ":" suite]
    
  • try
    try_stmt  ::=  try1_stmt | try2_stmt
    try1_stmt ::=  "try" ":" suite
                   ("except" [expression [("as" | ",") identifier]] ":" suite)+
                   ["else" ":" suite]
                   ["finally" ":" suite]
    try2_stmt ::=  "try" ":" suite
                   "finally" ":" suite
    
  • with
    with_stmt ::=  "with" with_item ("," with_item)* ":" suite
    with_item ::=  expression ["as" target]
    
  • definição de funções
    decorated      ::=  decorators (classdef | funcdef)
    decorators     ::=  decorator+
    decorator      ::=  "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE
    funcdef        ::=  "def" funcname "(" [parameter_list] ")" ":" suite
    dotted_name    ::=  identifier ("." identifier)*
    parameter_list ::=  (defparameter ",")*
                        (  "*" identifier ["," "**" identifier]
                        | "**" identifier
                        | defparameter [","] )
    defparameter   ::=  parameter ["=" expression]
    sublist        ::=  parameter ("," parameter)* [","]
    parameter      ::=  identifier | "(" sublist ")"
    funcname       ::=  identifier
    
  • definição declasses
    classdef    ::=  "class" classname [inheritance] ":" suite
    inheritance ::=  "(" [expression_list] ")"
    classname   ::=  identifier
    

Exemplo do try – finally

>>> def f():
...     a = "Not a number"
...     try:
...         a = 1/0
...     finally:
...         return a
...
>>> f()
'Not a number'
>>>
>>> def g():
...     a = "Not a number"
...     try:
...         a = 10/3
...     finally:
...         return a
...
>>> g()
3

Exemplo de aplicação da instrução for

>>> a=range(10)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for x in a[:]:
...     if x > 3: a.remove(x)
...
>>> a
[0, 1, 2, 3]

Instruções

A instrução de atribuição do Python permite ter uma lista como destino da atribuição. Já tinha visto noutras linguagens: no PHP e no Prolog. Eis um exemplo em Python, onde a atribuição à lista a,b premite fazer a troca das variáveis, sem variável auxiliar:

>>> a=2
>>> b=5
>>> a,b=b,a
>>> print a, b
5 2

A instrução pass é uma instrução nula, não executa nenhuma operação, e é útil para quando a sintaxe obriga à existência de uma instrução num local onde não pretendemos ação alguma.

def f(arg): pass    # a function that does nothing (yet)

class C: pass       # a class with no methods (yet)

A instrução del pemite apagar uma lista de objetos. As variáveis a e b do exemplo acima, podem ser apagadas da forma seguinte:

del a,b

A instrução print imprime um objeto ou uma lista de objetos.

>>> a=2
>>> b=5
>>> print a, b
2 5

A instrução return permite devolver um objeto ou uma lista de objetos, como resultado de uma função.

A instrução yield é usada com funções geradoras. Para perceber a instrução yield é preciso perceber o que são funções geradoras (generators). Uma função geradora é semelhante a um iterável, mas é criada on-the-fly, e o seu valor só pode ser usado uma vez, pois não fica guardado. A instrução yield faz o retorno de uma finção geradora e, nesse contexto, é semelhante a um return.

>>> def createGenerator():
...     mylist = range(3)
...     for i in mylist:
...         yield i*i
...
>>>
>>> mygenerator = createGenerator() # create a generator
>>>
>>> print(mygenerator) # mygenerator is an object!

>>> for i in mygenerator:
...      print(i)
...
0
1
4

As instruções break e continue podem ser usadas apenas no âmbito de instruções for e while.

A instrução global cria identificadores globais.

A instrução exec permite a execução dinâmica de código Python. Segue-se um exemplo de execução de código que está contido numa string.

>>> mycode = 'print "hello world"'
>>> exec(mycode)
hello world

Expressões e curiosidades

Começo pela curiosidade. E digo curiosidade, porque penso que é a primeira linguagem de programação onde vejo esta construção. Há outras linguagens pelas quais nunca incursei, como o Ruby, ou incursei pouco, como o Perl, que talvez permitam este tipo de construções, mas nunca vi. A sintaxe seguinte permite criar uma lista de valores que é um subconjunto de uma progressão aritmética.

>>> a = 42
>>> b = list(a + i for i in range(10))
>>> b
[42, 43, 44, 45, 46, 47, 48, 49, 50, 51]

Ou ainda:

>>> list(map(lambda x: 2*x, (a + i for i in range(10))))
[84, 86, 88, 90, 92, 94, 96, 98, 100, 102]

Passemos às expressões. Nas expressões aritméticas, as conversões são feitas sempre para cima, antes de operar. “Para cima” significa, que entre dois operandos de tipos diferentes, a conversão é feita para o tipo “maior”, sendo a sequência crescente a seguinte: int, long, float, complex. Os booleanos são inteiros. A unidade imaginária é o j.

>>> 5+True
6
>>> True + (2.5+3j)
(3.5+3j)

Segundo o manual do Python, as expressões podem ser as seguintes, sendo algumas delas apresentadas em sintaxe BNF:

  • Conversões aritméticas
    • Átomos
    • Identificadores
    • Literais
    • Lista entre parêntesis
    • Listas
    • Conjuntos e dicionários
    • Expressões geradoras
    • Conversões de strings
    • Expressões “yield”
  • Operações primárias
    primary ::=  atom | attributeref | subscription | slicing | call
    • Referências a atributos
    • Indexação
    • Cortes
    • Chamadas
  • Potência
    power ::=  primary ["**" u_expr]
  • Operações unárias
    u_expr ::=  power | "-" u_expr | "+" u_expr | "~" u_expr
  • Operações aritméticas
    m_expr ::=  u_expr | m_expr "*" u_expr | m_expr "//" u_expr | m_expr "/" u_expr
                | m_expr "%" u_expr
    a_expr ::=  m_expr | a_expr "+" m_expr | a_expr "-" m_expr
  • Operações de deslocamento
    shift_expr ::=  a_expr | shift_expr ( "<<" | ">>" ) a_expr
  • Operações bit a bit
    and_expr ::=  shift_expr | and_expr "&" shift_expr
    xor_expr ::=  and_expr | xor_expr "^" and_expr
    or_expr  ::=  xor_expr | or_expr "|" xor_expr
  • Comparações
    comparison    ::=  or_expr ( comp_operator or_expr )*
    comp_operator ::=  "<" | ">" | "==" | ">=" | "<=" | "<>" | "!="
                       | "is" ["not"] | ["not"] "in"
  • Operações booleanas
    or_test  ::=  and_test | or_test "or" and_test
    and_test ::=  not_test | and_test "and" not_test
    not_test ::=  comparison | "not" not_test
  • Expressões condicionais
    conditional_expression ::=  or_test ["if" or_test "else" expression]
    expression             ::=  conditional_expression | lambda_expr
  • Lambdas
    lambda_expr     ::=  "lambda" [parameter_list]: expression
  • Expressões com listas
    expression_list ::=  expression ( "," expression )* [","]

A tabela de precedências dos operadores, do mais fraco para o mais forte, é a seguinte:

Operator Description
lambda Lambda expression
ifelse Conditional expression
or Boolean OR
and Boolean AND
not x Boolean NOT
in, not in,
is, is not, <,
<=, >, >=, <>, !=, ==
Comparisons, including membership
tests and identity tests
| Bitwise OR
^ Bitwise XOR
& Bitwise AND
<<, >> Shifts
+, – Addition and subtraction
*, /, //, % Multiplication, division, remainder
[8]
+x, -x, ~x Positive, negative, bitwise NOT
** Exponentiation [9]
x[index], x[index:index],
x(arguments…), x.attribute
Subscription, slicing,
call, attribute reference
(expressions…),
[expressions…],
{key: value…},
`expressions…`
Binding or tuple display,
list display,
dictionary display,
string conversion

Funções predefinidas II

Eis a lista de funções predefinidas do Python atualmente.

Segue-se a exposição de algumas dessas funções de âmbito mais geral. As primeiras funções já foram apresentadas no artigo anterior.

As funções chr() e ord() convertem uma letra ASCII no seu valor numérico e vice-versa.

>>> chr(65)
'A'
>>> ord('B')
66
>>> chr(225)
'\xe1'

As funções complex(), str(), int(), long() e float() convertem string para número ou vice-versa.
A função hex() converte um inteiro para o formato hexadecimal.
A função oct() converte um inteiro para o formato octal.

>>> complex("5+2j")
(5+2j)
>>> str(3+6)
'9'
>>> int("4")
4
>>> float("3.14")
3.14
>>> hex(1234)
'0x4d2'
>>> oct(123)
'0173'

divmod(a,b) calcula a divisão inteira e o resto da divisão inteira entre dois números (a//b,a%b).

>>> divmod(7,3)
(2, 1)

enumerate(sequence, start=0) devolve uma sequência enumerada

>>> seasons = ['Spring', 'Summer', 'Fall', 'Winter']
>>> list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
>>> list(enumerate(seasons, start=1))
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

A função eval() é semelhante à função homónima do Javascript: avalia uma expressão contida numa string.

>>> x = 1
>>> print eval('x+1')

O comando exec() permite executar código contido numa string. A função execfile() executa código contido num ficheiro.

>>> exec("x=3")
>>> print x
3

As funções file() e open() permitem criar e/ou abrir um ficheiro, mas a última é preferível.
A função format(value[, format_spec]) converte um valor para um formato definido por format_spec (ver documentação).

>>> s = "python string!"
b = "i am a {0}".format(s)
>>> a =  "i am a %s" % s
print "--{:^30}--".format(s)
>>> b = "i am a {0}".format(s)
>>> print a
'{0}{1}{0}'.format('abra', 'cad')
i am a python string!

>>> print b
i am a python string!
>>> print "--{:^30}--".format(s)
'Coordinates: {latitude}, {longitude}'.format(**coord)

--        python string!        --
>>>
>>> '{2}, {1}, {0}'.format('a', 'b', 'c')
'c, b, a'
>>>
>>> '{0}{1}{0}'.format('abra', 'cad')
'abracadabra'
>>>
>>> 'Coordinates: {latitude}, {longitude}'.format(latitude='37.24N', longitude='-115.81W')
'Coordinates: 37.24N, -115.81W'
>>>
>>> coord = {'latitude': '37.24N', 'longitude': '-115.81W'}
>>> 'Coordinates: {latitude}, {longitude}'.format(**coord)
'Coordinates: 37.24N, -115.81W'

globals() devolve um dicionário (objeto JSON) com a tabela de símbolos globais.
locals() devolve um dicionário (objeto JSON) com a tabela de símbolos locais.

>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, '__package__': None}
>>> locals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, '__package__': None}

len() devolve o número de elementos de um objeto.

>>> len("Olá")
3
>>> len([1,3,5])
3
>>> len(globals())
4

map(function, iterable, …) aplica uma função aos valores de um iterável.
max(iterable[, key]) ou max(arg1, arg2, *args[, key]) determinam o máximo de um conjunto de elementos.
min(iterable[, key]) ou min(arg1, arg2, *args[, key]) determinam o máximo de um conjunto de elementos.
sum(iterable[, start]) soma os elementos de um iterável.

>>> map(lambda x: 2*x, [1,2,4,6,2,3])
[2, 4, 8, 12, 4, 6]
>>> max([1,2,4,6,2,3])
6
>>> min([1,2,4,6,2,3])
1
>>> sum([1,2,4,6,2,3])
18

next(iterator[, default]) obtém o próximo elemento de um iterador.

pow(x, y[, z]) calcula a potÊncia de x elevado a y módulo z, de forma eficiente. z é opcional, e nesse caso pode calcular-se x ** y.

>>> pow(2,3,5)
3
>>> pow(2,3)
8
>>> 2**3
8

print(*objects, sep=’ ‘, end=’\n’, file=sys.stdout) imprime objetos num fluxo (stream).

range() cria um intervalo de inteiros.

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(5,10)
[5, 6, 7, 8, 9]
>>> range(0, -10, -1)
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]

O Python foi criado com o nome de um grupo de animadores britânicos, tal como se percebe pelo exemplo da função raw_input().

>>> s = raw_input('--> ')
--> Monty Python's Flying Circus
>>> s
"Monty Python's Flying Circus"

reduce(function, iterable[, initializer]) aplica uma função de dois arguemntos, iterativamente a um iterável. Por exemplo, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calcula ((((1+2)+3)+4)+5).

>>> def soma(x,y):
...   return x+2*y
...
>>> reduce(soma, [1, 2, 3, 4, 5])
29

round() arredonda um número.

>>> round(3.14)
3.0
>>> round(3.14,1)
3.1
>>> round(3.7)
4.0

frozenset, list, set, tuple e dict são funções que permitem criar iteráveis ou coleções.

sorted(iterable[, cmp[, key[, reverse]]]) ordena um iterável.

>>> sorted( [1,2,4,6,2,3], cmp=lambda x,y: cmp(x, y) )
[1, 2, 2, 3, 4, 6]
>>> sorted( [1,2,4,6,2,3], cmp=lambda x,y: cmp(y, x) )
[6, 4, 3, 2, 2, 1]

tuple([iterable]) cria um tuplo a partir de um iterável.

>>> tuple("xpto")
('x', 'p', 't', 'o')

A função type(object) obtém o tipo de um objeto.

>>> type("xpto")
<type 'str'>
>>> type([1,2,4,6,2,3])
<type 'list'>
>>> type(3+6j)
<type 'complex'>

A função type(name, bases, dict) pode ser usada com 3 prâmetros. Nesse caso cria uma classe. As duas instruções seguintes são equivalentes.

>>> class X(object):
...     a = 1
...
>>> X = type('X', (object,), dict(a=1))

unichr(i) devolve a string Unicode, cujo código é i.

zip([iterable, …]) cria uma lista de tuplos emparelhados pelo índice dos argumentos. A lista gerada tem o comprimento do iterável mais curto.

>>> zip([1,2,3],"abc")
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> zip([1,2,3,4,5,6],"xpto")
[(1, 'x'), (2, 'p'), (3, 't'), (4, 'o')]

Funções predefinidas

Para além do manual de referência, que é relativamente árido nalguns capítulos, o Python tem um manual sobre a Biblioteca Standard, que começa por apresentar as funções predefinidas da linguagem.

A função abs() calcula o valor absoluto de inteiros, reais e complexos.

>>> abs(-3)
3
>>> abs(-7.5)
7.5
>>> abs(3+4j)
5.0

A função all() verifica se todos os elementos de um iterável são verdade.
A função any() verifica se algum elemento de um iterável é verdade.

>>> all([1,0])    # array composto por 1 e 0
False
>>> all([1,4])    # array composto por 1 e 4
True
>>> any([1,0])
True
>>> any([0,0,False])
False
>>> any([True,False])
True

O tipo abstrato basestring() não pode ser instanciado, é a superclasse de str e unicode, mas pode ser usado para testar se um objeto é do tipo str ou unicode.

>>> isinstance("Ola", basestring)
True
>>> isinstance("Olá Mundo", basestring)
True
>>> isinstance(123, basestring)
False

A função bin() devolve uma string com a representação binária de um inteiro. Ainda ninguém desafiou o guru do Python a mostrar a representação binária de um real? Em C pode ser feita assim:

float x = 3.5;
printf("%X", *(int*)&x);

bool() avalia um objeto como booleano.

>>> bin(123)
'0b1111011'
>>> bool(2)
True
>>> bool(0)
False
>>> bool()
False

A função bytearray() devolve o array de bytes que compõem uma string. Se a string estiver em unicode, é necessário passar a codificação como parâmetro. Ver documentação.

>>> bytearray("abc")
bytearray(b'abc')
>>>
>>> s = "Olá Mundo"
>>> ":".join("{:02x}".format(ord(c)) for c in s)
'4f:6c:e1:20:4d:75:6e:64:6f'
>>>
>>> s = bytearray("Olá Mundo")
>>> ":".join("{:02x}".format(c) for c in s)
'4f:6c:e1:20:4d:75:6e:64:6f'

Mais funções no próximo artigo.