====== Python - elementos del lenguaje ======
//Clases, módulos y funciones própias de Python.//
===== Cómo tratar con... =====
==== Strings ====
=== Acciones básicas ===
my_str = 'Hola buenos días!' # Definición de un string
'ola' in my_str # Saber si un string forma parte de otro string
my_str[-1:] # Recoger el último caracter
my_str[2] # Recoger el segundo carácter
my_path = u"c:\windows" # Omite los carácteres de escape
len(my_str) # Saber el tamaño del string
my_str + my_str2 # Concatena el string my_str con my_str2
=== Métodos del string ===
* ''upper()'': Pasa a mayúsculas.
* ''startswith(t)''
* ''endswith(t)''
* ''islower()''
* ''isupper()''
* ''isalpha()'', si todos los caráceres son letras.
* ''isalnum()'', si todos los carácteres son alfanuméricos (letra o número).
* ''isdigit()'', si todos los carácteres son dígitos.
* ''istitle()'', si todas las palabras inician con mayusculas.
* ''find(s)'', devuelve el primer índice de s, si no existe devolverá -1. ''rfind(s)'' devolverá el último índice.
* ''replace(s1, s2)'', reemplaza s1 por s2.
* ''encode(s)'', devolverá el string con la codificación indicada como s.
* ''strip()'', hace un trim del string.
* ''capitalize()'', pone la primera letra en mayúsuculas.
=== Formatear strings ===
Modo antiguo:
"La capital de %s es %s" % ("Araba", "Gasteiz") # 'La capital de Araba es Gasteiz'
"%s tiene %d provincias y %4d habitantes" % ("Araba", 1, 100) # 'Gasteiz tiene 1 provincias y 0100 habitantes'
"Cada uno con %.2f ojos" % (2.5457) # 'Cada uno con 2.54 ojos'
Modo nuevo, con método ''format'':
'{0} and {1}'.format('spam', 'eggs') # spam and eggs
'This {food} is {adjective}.'.format(food='spam', adjective='absolutely horrible') # This spam is absolutely horrible.
'The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred', other='Georg') # The story of Bill, Manfred, and Georg.
'The value of PI is approximately {0:.3f}.'.format(math.pi) # The value of PI is approximately 3.142.
'{0:10} ==> {1:10d}'.format('Jack', 4098) # Jack ==> 4098
Otra forma:
str = 'Name: {user.name}, Age: {user.age}, Sex: {user.sex}'.format(user=user)
Podemos hacer uso de ''!s'' y ''!r'' que llaman al str() y al repr() del objeto. Combinándolo queda:
subscriber.connect("tcp://127.0.0.1:{port!s}".format(port=ZPORT))
subscriber.connect("tcp://127.0.0.1:{0!s}".format(ZPORT))
=== Carácteres de escape ===
* ''\u'', indica que lo que sigue es un carácter unicode.
=== Definiciones especiales de strings ===
u'string' # indica que el string contiene carácteres unicode
r'string' # indica que el string es raw (que no se tendrán en cuenta los carácteres de escape)
s1 = '''Esto es un
string multilínea''' # crea un string multilínea
=== Otras acciones ===
# Unir un vector en un string:
" ".join(['a','b','c']) # Sacaría: 'a b c'
":".join(['a','b','c']) # Sacaría: 'a:b:c'
* Existen funciones para el [[script:python:new:api#expresiones_regulares|tratamiento de expresiones regulares]].
==== Fechas ====
=== datetime ===
from datetime import datetime
datetime.now() # momento actual
datetime.time(datetime.now()) # hora actual
datetime.date(2006, 5, 6).isoformat() # crea un datetime y lo devuelve como string en un formato fácilmente legible (2006-05-06)
=== struct_time ===
import time
time.localtime() # recoge el momento actual
time.ctime(longValue/1000) # crea una fecha a partir de un long
=== Comparación de fechas ===
Comparación utilizando objetos ''datetime'':
now = datetime.datetime.now()
today8am = now.replace(hour=8, minute=0, second=0, microsecond=0)
now < today8am
now == today8am
now > today8am
Cmparación utilizando ''timedelta'':
from datetime import datetime, timedelta
then = datetime.now () - timedelta (hours = 2)
now = datetime.now ()
(now - then) > timedelta (days = 1)
(now - then) > timedelta (hours = 1)
==== Ficheros ====
La llamada a la función ''open()'' devuelve un objeto del tipo ''File'', los argumentos son la ruta del fichero y el modo. \\
El modo puede ser 'r' para leer (si se omite es el que se utiliza por defecto), 'w' para escribir (truncando si ya existe) y 'a' para agregar. \\
Podemos agregar 'b' al modo, esto hará que abramos un fichero en modo binario.
var = open ("c:\\prueba.txt")
for file in var.readlines():
print file,
var.close()
=== Funciones y propiedades ===
* ''close()'', cierra el fichero.
* ''read()'', lee hasta el final del fichero.
* ''read(i)'', lee ''i'' carácteres a partir de la última lectura.
* ''fileno()'', retorna el número del descriptor del fichero.
* ''seek(i)'', se mueve ''i'' bytes.
* ''tell()'', indica la posición del fichero.
* ''write(s)''
* ''writelines(lst)''
* ''closed''
* ''encoding''
==== Árbol de directorios ====
=== Mediante os ===
* [[http://docs.python.org/2/library/os.html|Doc]]
import os
os.path.abspath(rel_path) # Devuelve el path absoluto al que apunta rel_path
os.path.basename(obs_path) # Devuelve el nombre del fichero
os.mkdir(path) # Crea un directorio
os.sep # Devuelve el separador de directorios del sistema
os.listdir(path) # Devuelve una lista con los nombres de archivos
os.path.exists(path) # Indica si un fichero existe
os.path.isdir(path) # Indica si es directorio
os.path.isfile(path) # Indica si es fichero
os.path.getsize(path) # Devuelve el tamaño de un fichero
os.remove(path) # Borra un fichero
=== Mediante shutil ===
import shutil
shutil.rmtree(path) # Elimina un directorio
shutil.copytree(src, dst) # Copia un directorio
shutil.copyfile(src, dst) # Copia un fichero
shutil.move(src, dst) # Mueve un fichero
=== Recorrer árbol de directorios ===
import os
path = raw_input("Specify a folder that you want to perform an 'os.walk' on: >> ")
for root, dirs, files in os.walk(path):
print root
print dirs
print files
print "---------------"
==== Parámetros del ejecutable ====
=== Argparse ===
* [[http://docs.python.org/dev/library/argparse.html|Documentación]]
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('-f', '--foo', dest='foo', help='foo help')
args = parser.parse_args()
print args.foo
''add_argument'' permite, entre otros, los siguientes parámetros:
* ''name'' o ''flags'': un nombre o lista de opciones, ejemplos: ''foo, -f, --foo''
* ''action'': una acción especial para ese argumento (ver ayuda).
* ''store_true'' o ''store_false'': ''store_true'' significa que si se especifica la opción entonces se asignará ''True'' al valor de este: ''parser.add_argument("--verbose", help="increase output verbosity", action="store_true")''
* ''default'': el valor por defecto.
* ''parser.add_argument('--foo', default=42)''
* ''type'': el tipo de la variable.
* ''parser.add_argument('--foo', type=int)''
* ''choices'': posibles opciones:
* ''parser.add_argument('foo', type=int, choices=range(5, 10))''
* ''parser.add_argument('move', choices=['rock', 'paper', 'scissors'])''
* ''required'': indica si el argumento es obligatorio
* ''parser.add_argument('--foo', required=True)''
* ''help'': texto de ayuda
* ''metavar'': el nombre del argumento dentro de la ayuda.
* ''dest'': el atributo de destino.
El constructor de ''ArgumentParser'' acepta los siguientes parámetros:
* ''prog'': el nombre dle programa.
* ''usage'': el string de ayuda.
* ''parents'': array de ArgumentParser de los cuales sus argumentos se incluirán junto con los de este.
=== OptionParse ===
* :!: Deprecated!
Se utiliza el objeto ''OptionParse'' del paquete ''optparse''
from optparse import OptionParser
# Crear objeto OptionParser e indicar el uso del ejecutable
parser = OptionParser(usage="SRCDS.py -a ADDR -p RCONPASS [command]")
# Añadir parametros a gestionar
parser.add_option("-p",dest="rcon",default="",help="Specifies the rcon password")
parser.add_option("-a",dest="addr",default="",help="Specifies the address of the server to connect to")
# Parsear parametros
(options,args) = parser.parse_args()
# Tratar parametros
if not options.addr:
os.system(sys.argv[0] + " -h")
sys.exit(1)
print("Connecting to %s with rcon password of %s" % (options.addr,options.rcon))
=== Notas ===
* [[http://docopt.org/|docopt]] es una librería que podría ser utilizada para ello.
==== Sockets ====
=== Broadcasting ===
* Todas las máquinas de la red reciben información.
* Pueden haber varios programas escuchando por el mismo puerto.
Emisor:
MYPORT = 5000
import sys, time
from socket import *
s = socket(AF_INET, SOCK_DGRAM)
s.bind(('', 0))
s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
while 1:
data = repr(time.time()) + '\n'
s.sendto(data, ('', MYPORT))
print 'send!', data
time.sleep(2)
Receptor:
import socket
import struct
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', 5000))
while True:
print sock.recv(10240)
==== Threads ====
* [[http://docs.python.org/2/library/threading.html]]
Para utilizarlos importaremos el módulo ''threading''. \\
Podemos llamar a una función haciendo:
thread = threading.Thread(target=myFunc)
thread.start()
Un thread tiene la propiedad ''daemon'', si esta es ''True'' cuando el thread padre acabe el thread hijo también lo hará (pero cuidado, esto puede hacer no liberar el recurso que estaba utilizando un objeto ''Event'' (poniéndolo como flag en el bucle principal).
* Los objetos ''Event'' sirven para comunicarse entre threads. Consisten en una flag que puede estar a ''True'' o a ''False'' sus métodos importantes on:
* ''wait'', que hará esperar hasta que la flag sea ''True''.
* ''set'', que pondrá la flag a ''True''.
* ''isSet'' o ''is_set'' que indicará el estado de la flag.
* ''reset'' que pondrá la flag a ''False''.
* Los objetos ''Lock'' sirven para indicar una zona de exclusión mutua. Utilizaremos su método ''acquire'' para adquirirla y el ''release'' para liberarla:
self.mutex = Lock()
def initProcess (self):
self.mutex.acquire()
host = self.results.get()
self.mutex.release()
return host
==== Múltiples procesos ====
En el módulo [[http://docs.python.org/dev/library/multiprocessing|multiprocessing]] podemos encontrar clases como ''Process'', para lanzar un proceso a parte, ''Pool'', para lanzar varios procesos, ''Pipe'' o ''Queue'' para la comunicación entre ellos...
=== Lanzar procesos ===
La clase ''Pool'' permite lanzar varios procesos, en el constructor se le pasa el número de procesos que gestionará y al llamar al método ''map'' se le indicará qué función corresponderá a qué proceso y el array de parámetros para cada uno de ellos:
from multiprocessing import Pool
servers = config['servers'] # Servers array de diccionarios
procs = Pool(processes=len(servers))
procs.map(askServerInfo, servers) # askServer es una función que recibe un único parámetro
=== Comunicación entre procesos ===
* Pipe o Queue
=== Compartir datos entre procesos ===
* Array o value
=== Sincronización ===
* Lock
=== Casos ===
Uso de ''Queue'' y procesos sueltos:
from multiprocessing import Queue, Process
class AskServerInfo (Process):
def __init__(self, server, queue):
Process.__init__(self)
self.server = server
self.queue = queue
def run (self):
# action to obtain info from self.server
self.queue.put(self.server)
if __name__ == "__main__":
config = readConfigFile('servers.yml')
nservers = len(config['servers'])
results = Queue()
for server in config['servers']:
worker = AskServerInfo(server, results)
worker.start()
while nservers:
info = results.get()
saveInfo(info)
nservers = nservers - 1
==== Ejecución de procesos externos ====
* [[http://docs.python.org/library/subprocess.html]]
==== Expresiones regulares ====
=== Uso ===
import re
for w in wordlist:
print re.search('^..j', w) # imprime None o un objeto patrón encontrado según si w cumple el patrón
[w for w in wordlist if re.search('^..j', w)] # devuelve lista de palabras que su tercer carácter sea la j
word = 'supercalifragilisticoespialidoso'
re.findall(r'[aeiou]', word) # devuelve la lista de vocales directamente (sin necesidad de recorrer el resultado)
re.split(regex, text) # corta un texto a partir de una expresión regular
re_pattern='(/+)|(~+)|("+)|(;+)|(,+)|(!+)|(\.)+|(:+)|(\(+)|(\)+)'
re.sub(re_pattern, ' ', texto) # substituye un patrón en el texto
=== Selección del retorno ===
Los paréntesis nos permiten indicar qué se ha de retornar en caso de encontrar un matching. Agregando ''?:'', '':?'', ''?'' al principio o al final de estos podremos indicar qué parte del matching nos interesa.
=== Selección de palabras ===
Mediante ''<'' y ''>'' podemos seleccionar palabras:
* ''(<.*>)'' devolverá los adjetivos que se le agregan a //"a man"// (nervous, dangerous, white...).
==== Codificación de texto ====
=== Funciones nativas ===
unicode('abcdef') # devuelve el string en formato unicode: u'abcdef'
s.encode('utf8') # devuelve el string s en formato utf-8
unicode_char = u'\u0061' # asigna un string con codificación unicode, en este caso unicode_char tomaría el valor 'a'
unicode('\x80abc', errors='strict') # errors puede tomar los valores 'strict', 'replace' o 'ignore'
=== codecs ===
import codecs
f1 = codecs.open(path1, encoding='latin2') # abre un archivo para leer con una codificación latin2
f2 = codecs.open(path2, 'w', encoding='utf-8') # abre un archivo para escribir codificandolo en utf-8
=== unicodedata ===
title = u"Klüft skräms inför på fédéral électoral große"
import unicodedata
unicodedata.normalize('NFKD', title).encode('ascii','ignore')
'Kluft skrams infor pa federal electoral groe'
==== Formato de texto ====
=== Formato de tags en general ===
Encontraremos una serie de parsers en el módulo [[http://docs.python.org/library/sgmllib.html|sgllib]].
=== HTML ===
=== XML ===
* {{script:python:pruebas_xml.zip|Ejemplos de trato de XML}}
import xml.dom.minidom
archivo = xml.dom.minidom.parse('archivo.xml') # cargar el archivo
archivo.toxml() # mostrar el código xml
elements = archivo.getElementsByTagName('name') # recoge los nodos con nombre name
nodo = elements.item(0) # recoge el primer elemento
nodo.hasChildNodes() # devuelve si tiene nodos hijos
nodo.nodeName # nombre del nodo
nodo.nodeType # tipo del nodo
nodo.nodeValue # valor del nodo
subnds = nodo.childNodes # devuelve los nodos hijo del nodo actual
attr = nodo.attributes # devuelve los atributos del nodo actual
subn = attr.getNamedItem('tal') # retorna el atributo 'tal' del nodo actual
xmldoc.getElementsByTagName('title')[0].firstChild.data
from xml.dom.minidom import parseString
file = open('somexmlfile.xml','r')
data = file.read()
file.close()
dom = parseString(data)
Si el nodo actual tiene un valor, del estilo ''nom''; el nodo actual tendrá como ''nodeName'' "nombre" pero ''None'' como ''nodeValue'', para acceder a su valor tendrás que coger sus nodos-hijo y el primer elemento de estos tendrá el ''nodeName'' a ''None'' pero el ''nodeValue'' como "nom". \\
Para más información lee //"Python and XML - An introduction.pdf"// en los [[script:python:docs#xml|documentos]].
=== JSON ===
A partir de la versión 2.6 de Python podemos utilizar un encoder\decoder de [[http://docs.python.org/library/json.html|JSON en Python]]. \\
Para codificar a JSON:
import json
json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}]) # '["foo", {"bar": ["baz", null, 1.0, 2]}]'
Para decodificar de JSON:
import json
json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') # [u'foo', {u'bar': [u'baz', None, 1.0, 2]}]
==== Contenido web ====
=== urllib ===
from urllib import urlopen
web = urlopen('http://www.google.es') # abre un stream web
web.read() # lee la web indicada
web.close() # cierra el stream de la web
* Si utilizamos uno o varios proxies podemos pasarselos como segundo parámetro en una lista a ''urlopen''.
==== Serialización ====
Para su uso utilizaremos el módulo [[http://docs.python.org/library/pickle.html|pickle]].
==== SQLite ====
Desde la versión 2.5, y de forma nativa, en Python podemos utilizar el módulo ''sqlite3'' el cual nos permite hacer uso de este motor de base de datos.
import sqlite3
db = sqlite3.connect('c:\\prueba.db') # abrimos una conexión a un fichero de base de datos
c1 = db.cursor() # recogemos un cursor de acceso a la DB
c1.execute('insert into data (id) values (1)') # indicamos el comando
db.commit() # tras un comando se realiza un commit
c2 = db.cursor()
c2.execute('select * from data') # indicamos la consulta a lanzar
for row in c2: # recorremos el cursor
print row
c3 = db.cursor()
c3.execute('select * from data')
lst = c3.fetchall() # extrae el resultado en una lista
c1.close() # cerramos la primera conexión
Al hacer el connect podemos conectar con una DB ficticia en memoria usando ''":memory:"''.
=== Insertar una fecha ===
t = time.localtime()
ts = sqlite3.Timestamp(t[0], t[1], t[2], t[3], t[4], t[5]);
c.execute ("insert into fecha (fecha) values (?)", (ts,))
=== Insertar un blob ===
query = u'''insert into data VALUES(?, ?)'''
c = db.cursor()
b = sqlite3.Binary(data)
c.execute(query,(id,b))
db.commit()
==== Datos binarios ====
=== Módulo binascii ===
Existe el módulo ''binascii'' que trae distintas funciones para recoger los datos en un formato u otro:
import binascii
binascii.b2a_base64(data)
binascii.b2a_hex(data)
binascii.b2a_uu(data)
binascii.crc32(data, 0)
binascii.hexlify(data)
=== Módulo array ===
* [[http://docs.python.org/library/array.html]]
También podemos hacerlo mediante el módulo ''array'', este define un objeto con un tipo concreto (caracteres, integers, floats...) el cual se indica en la creación que es de la siguiente forma:
import array
a = array.array('c', data)
Siendo el primer parámetro pasado (''c'' en este caso) el tipo que es. Los tipos se definen por (c (caracter a partir de un byte), b, B, u, h (short a partir de dos bytes), H, i (integer a partir de dos bytes), I, l (long a partir de cuatro bytes), L, f (float a partir de cuatro bytes), d (doubles a partir de cuatro bytes)).
=== Módulo struct ===
* [[http://docs.python.org/library/struct.html]]
Muy útil para la extracción de datos tipados de una cadena en bytes, y viceversa, pasar datos a cadena de bytes. \\
Para recoger datos utilizamos la función ''unpack'' a la cual se le pasa un formato (que se forma a partir de los valores de la siguiente tabla) y la cadena de bytes:
^ format ^ tipo leido ^ numero de bytes ^
| c | char | 1 |
| b | byte | 1 |
| B | ubyte | 1 |
| h | short | 2 |
| H | ushort | 2 |
| i | int | 4 |
| I | uint | 4 |
| l | long | 4 |
| L | ulong | 4 |
| q | long (64b) | 8 |
| Q | ulong (64b) | 8 |
| f | float | ? |
| d | double | 8 |
| s | char[] | 1 |
| p | char[] | 1 |
Con este formato podríamos indicar qué es lo que viene en la cadena de bytes pasada, por ejemplo, ''cHIII'' esperaría a leer de los bytes un carácter, un unsigned short y tres unsigned int, es decir, la cadena de bytes debería de tener 11 bytes. El ejemplo también lo podríamos escribir como ''cH3I'', el resultado sería una lista. En el siguiente ejemplo se tiene un array de bytes (''data'') y se sabe que el primer es un número y el segundo un carácter. Luego también habría otro byte, pero este se imprimiría en la siguiente línea:
print struct.unpack("Bc", data[:2])
print struct.unpack("B", data[2:3])
El primer carácter del formato puede indicar hacia donde se dirigen los bytes, por ejemplo puede ser:
* ''!'': Como se envia por red.
* ''<'': Little-endian.
* ''>'': Big-endian.
Otras funciones útiles podrían ser ''calcsize'' que pasándole un formato indica qué tamaño debería de tener este:
struct.calcsize('hhl') # 8
O también podemos aprovechar el tipo ''namedtuple'' para crear objetos:
from collections import namedtuple
Student = namedtuple('Student', 'name serialnum school gradelevel')
Student._make(unpack('<10sHHb', data)) # Student(name='raymond ', serialnum=4658, school=264, gradelevel=8)
Y si quisieramos leer de un archivo binario...
import struct
f = file('test.chm','r')
s = f.read(4*1 + 3*4)
struct.unpack("4s3I", s) # ('ITSF', 3L, 96L, 1L)
==== Logging ====
* [[http://docs.python.org/2/library/logging.html]]
Niveles de log: ''DEBUG'', ''INFO'', ''WARNING'', ''ERROR'', ''CRITICAL'' \\
El nivel por defecto es ''WARNING''. \\
Desde cualquier módulo que se llame a logging se escribirá en el log general.
import logging
logging.warning('Watch out!')
logging.info('I told you so')
Esto tendrá la siguiente salida. No aparece la de info debido a que no se ha cambiado el nivel por defecto y root es el log por defecto también:
WARNING:root:Watch out!
=== Configuración básica ===
import logging
logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
Mediante los parámetros de ''logging.basicConfig'' podemos indicar:
* ''filename'': fichero de log.
* ''level'': nivel de log (''logging.DEBUG'', ''logging.WARNING''...)
* ''filemode'': según lo que se haga con el fichero (''w'' para reescribirlo cada vez que arranque).
* ''format'': Utilizando el {{http://docs.python.org/2/library/logging.html#logrecord-attributes|formato}} podemos indicar cosas como: '''%(asctime)s %(message)s''.
* ''datefmt'': Para indicar el formato de la fecha ('''%m/%d/%Y %I:%M:%S %p''').
=== Indicar varias fuetes ===
Podemos crear varios logs (a parte del root) para organizarlo:
logger1 = logging.getLogger('myapp.area1')
logger2 = logging.getLogger('myapp.area2')
logger1.debug('Quick zephyrs blow, vexing daft Jim.')
logger1.info('How quickly daft jumping zebras vex.')
logger2.warning('Jail zesty vixen who grabbed pay from quack.')
logger2.error('The five boxing wizards jump quickly.')
=== Indicar varios logs ===
Añadir, por ejemplo, la consola y darle otro formato (los sublogs también lo cogerán):
console = logging.StreamHandler()
console.setLevel(logging.INFO)
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)
logging.info('Jackdaws love my big sphinx of quartz.')
=== Configurar un log mediante un fichero ===
import logging
import logging.config
logging.config.fileConfig('logging.conf')
logger = logging.getLogger('simpleExample')
logger.debug('debug message')
Este sería el contenido de ''logging.conf'':
[loggers]
keys=root,simpleExample
[handlers]
keys=consoleHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
=== Para mostrar información de la excepción ===
try:
open('/path/to/does/not/exist', 'rb')
except (SystemExit, KeyboardInterrupt):
raise
except Exception, e:
logger.error('Failed to open file', exc_info=True)
=== Notas ===
Desactivar el log:
logging.getLogger().disabled = True
==== Imports dinámicos ====
A partir del módulo ''imp'' podemos realizar imports por código. Este módulo tiene funciones como...
* ''load_module'' para cargar el módulo.
También podemos hacerlo:
mod = __import__(name_module)
cls = getattr(mod, name_class)
O importar una ruta:
def import_path (mpath):
from imp import load_source
from os import path
fname = path.basename(obs_path)[:-3]
return load_source(fname, mpath)
=== Otra forma de importar ===
Si la aplicación se abre desde, por ejemplo, otro path.
import sys
from os.path import abspath
sys.path.append(abspath('.'))
from backend.css.tasks import parse_grammar
==== UnitTesting ====
* Deberemos hacer un import de ''unittest''.
* Las clases que implementen los tests heredarán ''unittest.TestCase''.
* Los tests serán los métodos de dicha clase, por convención su nombre empezará por ''test_''.
* Los ficheros de test también deberán tener un nombre que empiece por ''test_''.
* El método ''setUp(self)'' se ejecuta antes de cada test.
* El método ''tearDown(self)'' se ejecuta después de cada test.
* El siguiente comando realizará los tests dentro de ese directorio:
$ python -m unittest discover
* Otras formas de ejecutar tests:
$ python -m unittest test_random
$ python -m unittest test_random.TestSequenceFunctions
$ python -m unittest test_random.TestSequenceFunctions.test_shuffle
* Las comprobaciones que se pueden hacer son:
* ''assertEqual(expected, actual)''
* ''assertNotEqual(notExpected, actual)''
* ''assertTrue(expression)'' y ''assertFalse(expression)''
* ''assertIsInstance(object, class)''
'''
RULES OF TENNIS
* Each player can have either of these points in one game, described as 0-15-30-40. Each time a player scores, it advances of one position in the scale.
* A player at 40 who scores wins the set. Unless...
* If both players are at 40, we are in a *deuce*. If the game is in deuce, the next scoring player will gain an *advantage*. Then if the player in advantage scores he wins, while if the player not in advantage scores they are back at deuce.
'''
from tennis import Set, Scores
from unittest import TestCase
class TestSetWinning(TestCase):
def test_score_grows(self):
set = Set()
self.assertEqual("0", set.firstScore())
set.firstScores()
self.assertEqual("15", set.firstScore())
self.assertEqual("0", set.secondScore())
set.secondScores()
self.assertEqual("15", set.secondScore())
def test_player_1_win_when_scores_at_40(self):
set = Set()
set.firstScores(3)
self.assertEqual(None, set.winner())
set.firstScores()
self.assertEqual(1, set.winner())
def test_player_2_win_when_scores_at_40(self):
set = Set()
set.secondScores(3)
self.assertEqual(None, set.winner())
set.secondScores()
self.assertEqual(2, set.winner())
def test_deuce_requires_1_more_than_one_ball_to_win(self):
set = Set()
set.firstScores(3)
set.secondScores(3)
set.firstScores()
self.assertEqual(None, set.winner())
set.firstScores()
self.assertEqual(1, set.winner())
def test_deuce_requires_2_more_than_one_ball_to_win(self):
set = Set()
set.firstScores(3)
set.secondScores(3)
set.secondScores()
self.assertEqual(None, set.winner())
set.secondScores()
self.assertEqual(2, set.winner())
def test_player_can_return_to_deuce_by_scoring_against_the_others_advantage(self):
set = Set()
set.firstScores(3)
set.secondScores(3)
self.assertEqual(None, set.winner())
set.firstScores()
set.secondScores()
set.firstScores()
set.secondScores()
self.assertEqual(None, set.winner())
self.assertEqual("40", set.firstScore())
self.assertEqual("40", set.secondScore())
class TestScoreNames(TestCase):
def test_score_names(self):
scores = Scores()
self.assertEqual("0", scores.scoreName(0))
self.assertEqual("15", scores.scoreName(1))
self.assertEqual("30", scores.scoreName(2))
self.assertEqual("40", scores.scoreName(3))
self.assertEqual("A", scores.scoreName(4))
=== Notas ===
* [[https://wiki.python.org/moin/PythonTestingToolsTaxonomy|Librerías de TDD]]
===== Otros =====
==== Donde podemos encontrar... ====
* **Números aleatorios?**, en el módulo ''random''.
* ''random.choice'' escoge un elemento de la lista.
* ''random.randint'' devuelve un entero entre dos números (incluidos).
* **Un método de sleep?**, en el módulo ''time'' existe la función ''sleep''.
==== Funciones ====
* ''copy.copy'' copiará un objeto en otro.
* ''random.shuffle'' desordenará una colección.