Categorías
Python

Básico de entrada, de salida, y el formato de cadenas en Python

 

Tabla de Contenidos

  • El punto de interrupción () clases incorporadas
  • datos
  • personalización del módulo Atributos
  • Typing Mejoras
  • tiempo de precisión
  • Otros Pretty Cool características el Orden de Diccionarios está garantizado “asíncrono” y “lo esperan” son palabras clave “asyncio” Cara LiftContext VariablesImporting archivos de datos con “importlib.resources“desarrollador TricksOptimizations
  • La Orden de Diccionarios está garantizado
  • ‘asíncrono’ y ‘se espera la’ son palabras clave
  • ‘asyncio’ estiramiento facial
  • variables de contexto
  • Importación de archivos de datos con “importlib.resources”
  • Desarrollador trucos
  • optimizaciones
  • Por lo tanto, debo actualizar?
  • La Orden de Diccionarios está garantizado
  • “asíncrono” y “se espera la” son palabras clave
  • “asyncio” estiramiento facial
  • variables de contexto
  • Importación de archivos de datos con “importlib.resources”
  • Desarrollador trucos
  • Optimizaciones

Python 3.7 es lanzado oficialmente! Esta nueva versión de Python ha estado en desarrollo desde septiembre de 2016, y ahora todos nos llega a disfrutar de los resultados del trabajo de los desarrolladores del núcleo.

Lo que hace la nueva versión de Python llevar? Mientras que la documentación da una buena visión general de las nuevas características, en este artículo tendrá una inmersión profunda en algunas de las más grandes noticias. Estos incluyen:

  • acceso más fácil a los depuradores través de un nuevo punto de interrupción () incorporada
  • creación de clases simple utilizando las clases de datos
  • personalizada de acceso al módulo de atributos
  • Mejorado el soporte para el tipo insinuando
  • más altas funciones de temporización de precisión

más importante aún, Python 3.7 es rápida.

En las secciones finales de este artículo, podrá leer más acerca de esta velocidad, así como algunos de los otros que despliega de Python 3.7. También obtendrá algunos consejos sobre cómo actualizar a la nueva versión.

El punto de interrupción () incorporado

pesar de que podría esforzarse para escribir código perfecto, la simple verdad es que nunca lo hacemos. La depuración es una parte importante de la programación. Python 3.7 introduce la nueva incorporada la función de punto de interrupción (). Esto en realidad no añade ninguna funcionalidad nueva a Python, pero hace que el uso de depuradores más flexible e intuitiva.

Suponga que tiene el siguiente código erróneo en el bugs.py archivo:

def divide(e, f):
return f / e

a, b = 0, 1
print(divide(a, b))

Ejecución del código hace que un ZeroDivisionError dentro de la función de división (). Digamos que desea interrumpir su código y gota en un depurador a la derecha en la parte superior de división (). Puede hacerlo mediante el establecimiento de un llamado “punto de ruptura” en el código:

def divide(e, f):
# Insert breakpoint here
return f / e

Un punto de interrupción es una señal dentro de su código que la ejecución debe parada temporal, de modo que usted puede mirar a su alrededor en el estado actual del programa. ¿Cómo se coloca el punto de ruptura? En Python 3.6 y por debajo, se utiliza esta línea tanto críptica:

def divide(e, f):
import pdb; pdb.set_trace()
return f / e

Aquí, PDB es el depurador de Python de la biblioteca estándar. En Python 3.7, puede utilizar la nueva llamada punto de interrupción () funcionan como un acceso directo en su lugar:

def divide(e, f):
breakpoint()
return f / e

En el fondo, punto de interrupción () es la importación de primera pdb y luego llamar pdb.set_trace () para usted. Los beneficios obvios son que breakpoint () es más fácil de recordar y que sólo se necesita escribir 12 caracteres en lugar de 27. Sin embargo, la verdadera ventaja de utilizar punto de interrupción () es su capacidad de personalización.

Ejecutar la secuencia de comandos bugs.py con punto de interrupción ():

$ python3.7 bugs.py
> /home/gahjelle/bugs.py(3)divide()
-> return f / e
(Pdb)

El guión se romperá cuando alcanza punto de interrupción () y se pone en una sesión de depuración AP. Puede escribir C y pulsa enter para continuar la secuencia de comandos. Consulte la guía AP Nathan Jennings si desea obtener más información sobre el AP y la depuración.

Ahora, digamos que usted piensa que ha corregido el error. Que le gustaría ejecutar el script de nuevo, pero sin detenerse en el depurador. Se podría, por supuesto, comente la línea de punto de interrupción (), pero otra opción es utilizar la variable de entorno PYTHONBREAKPOINT. Esta variable controla el comportamiento de punto de interrupción (), y el establecimiento de PYTHONBREAKPOINT = 0 significa que cualquier llamada al punto de interrupción () se ignora:

$ PYTHONBREAKPOINT=0 python3.7 bugs.py
ZeroDivisionError: division by zero

Vaya, parece como si no se ha corregido el error después de todo …

Otra opción es PYTHONBREAKPOINT utilizar para especificar un depurador que no sea AP. Por ejemplo, para utilizar PuDB (un depurador visual en la consola) que puede hacer:

$ PYTHONBREAKPOINT=pudb.set_trace python3.7 bugs.py

Para que esto funcione, es necesario tener instalado pudb (PIP instalar pudb). Python se hará cargo de la importación pudb para usted sin embargo. De esta forma también se puede configurar el depurador predeterminado. Basta con establecer la variable de entorno PYTHONBREAKPOINT a su depurador preferido. Consulte esta guía para obtener instrucciones sobre cómo configurar una variable de entorno en el sistema. función

El nuevo punto de interrupción () no sólo trabaja con depuradores. Una opción podría ser conveniente comenzar simplemente un shell interactivo dentro de su código. Por ejemplo, para iniciar una sesión IPython, puede utilizar el siguiente:

$ PYTHONBREAKPOINT=IPython.embed python3.7 bugs.py
IPython 6.3.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: print(e / f)
0.0

También puede crear su propia función y tienen punto de interrupción (llamada) que. El siguiente código imprime todas las variables en el ámbito local. Agregar a un archivo llamado bp_utils.py:

from pprint import pprint
import sys

def print_locals():
caller = sys._getframe(1) # Caller is 1 frame up.
pprint(caller.f_locals)

Para utilizar esta función, ajuste PYTHONBREAKPOINT como antes, con la notación:.

$ PYTHONBREAKPOINT=bp_utils.print_locals python3.7 bugs.py
{'e': 0, 'f': 1}
ZeroDivisionError: division by zero

Normalmente, punto de interrupción () se utiliza para las funciones y los métodos de llamada que no necesitan argumentos. Sin embargo, es posible pasar argumentos también. Cambiar el punto de interrupción de línea () en bugs.py a:

breakpoint(e, f, end="<-END\n")

Nota: El depurador AP predeterminado levantará un TypeError en esta línea porque pdb.set_trace () no toma ningún argumento posicionales.

Ejecutar este código con punto de interrupción () haciéndose pasar por la función de impresión () para ver un ejemplo simple de los argumentos que se pasa a través de:

$ PYTHONBREAKPOINT=print python3.7 bugs.py
0 1<-END ZeroDivisionError: division by zero

Ver PEP 553, así como la documentación de punto de interrupción () y sys.breakpointhook () para más información. Clases

datos

El nuevo módulo dataclasses hace que sea más conveniente para escribir sus propias clases, como los métodos especiales como .__ init __ (), repr .__ __ (), y .__ eq __ () se agregan automáticamente. Usando el decorador @dataclass, puede escribir algo como:

from dataclasses import dataclass, field

@dataclass(order=True)
class Country:
name: str
population: int
area: float = field(repr=False, compare=False)
coastline: float = 0

def beach_per_person(self):
"""Meters of coastline per person"""
return (self.coastline * 1000) / self.population

Estas nueve líneas de código en soporte para un poco de código repetitivo y las mejores prácticas. Piense en lo que sería necesario para poner en práctica País como una clase regular: el método .__ init __ (), un repr, seis diferentes métodos de comparación, así como el método .beach_per_person (). Puede expandir el cuadro de abajo para ver una implementación del país que es más o menos equivalente a la clase de datos: aplicación

alternativo del "País" clase Mostrar / Ocultar

class Country:

def __init__(self, name, population, area, coastline=0):
self.name = name
self.population = population
self.area = area
self.coastline = coastline

def __repr__(self):
return (
f"Country(name={self.name!r}, population={self.population!r},"
f" coastline={self.coastline!r})"
)

def __eq__(self, other):
if other.__class__ is self.__class__:
return (
(self.name, self.population, self.coastline)
== (other.name, other.population, other.coastline)
)
return NotImplemented

def __ne__(self, other):
if other.__class__ is self.__class__:
return (
(self.name, self.population, self.coastline)
!= (other.name, other.population, other.coastline)
)
return NotImplemented

def __lt__(self, other):
if other.__class__ is self.__class__:
return ((self.name, self.population, self.coastline) < ( other.name, other.population, other.coastline )) return NotImplemented def __le__(self, other): if other.__class__ is self.__class__: return ((self.name, self.population, self.coastline) <= ( other.name, other.population, other.coastline )) return NotImplemented def __gt__(self, other): if other.__class__ is self.__class__: return ((self.name, self.population, self.coastline) > (
other.name, other.population, other.coastline
))
return NotImplemented

def __ge__(self, other):
if other.__class__ is self.__class__:
return ((self.name, self.population, self.coastline) >= (
other.name, other.population, other.coastline
))
return NotImplemented

def beach_per_person(self):
"""Meters of coastline per person"""
return (self.coastline * 1000) / self.population

Después de la creación, una clase de datos es una clase normal. Puede, por ejemplo, heredar de una clase de datos de la forma habitual. El objetivo principal de las clases de datos es hacer que sea rápido y fácil de escribir clases sólidas, en particular las pequeñas clases que principalmente almacenan datos.

Puede utilizar la clase de datos de País como cualquier otra clase:

>>> norway = Country("Norway", 5320045, 323802, 58133)
>>> norway
Country(name='Norway', population=5320045, coastline=58133)

>>> norway.area
323802

>>> usa = Country("United States", 326625791, 9833517, 19924)
>>> nepal = Country("Nepal", 29384297, 147181)
>>> nepal
Country(name='Nepal', population=29384297, coastline=0)

>>> usa.beach_per_person()
0.06099946957342386

>>> norway.beach_per_person()
10.927163210085629

Tenga en cuenta que todos los campos .name, .population, .area y .coastline se utilizan en la inicialización de la clase (aunque .coastline es opcional, como se muestra en el ejemplo de litoral Nepal). La clase El país tiene una repr razonable, mientras que la definición de métodos funciona igual que para las clases regulares.

Por defecto, las clases de datos se puede comparar a la igualdad. Puesto que especificamos fin = True en el decorador @dataclass, la clase País también se puede clasificar:

>>> norway == norway
True

>>> nepal == usa
False

>>> sorted((norway, usa, nepal))
[Country(name='Nepal', population=29384297, coastline=0),
Country(name='Norway', population=5320045, coastline=58133),
Country(name='United States', population=326625791, coastline=19924)]

La clasificación sucede en los valores de los campos, primero .name continuación .population, y así sucesivamente. Sin embargo, si se utiliza el campo (), puede personalizar los campos que será utilizada en la comparación. En el ejemplo, el campo .area se quedó fuera de la repr y las comparaciones.

Nota: Los datos de los países son de la CIA World Factbook con las cifras de población estimadas de julio de 2017.

Antes de ir todos reservar sus próximas vacaciones en la playa en Noruega, aquí es lo que dice el Factbook sobre el clima noruego: “templado a lo largo de la costa, modificada por la corriente del Atlántico Norte; interior más frío con el aumento de la precipitación y veranos más fríos; lluvias durante todo el año en la costa oeste “. clases

datos hacen algunas de las mismas cosas que namedtuple. Sin embargo, ellos obtienen su mayor inspiración del proyecto attrs. Vea nuestra guía completa para las clases de datos para más ejemplos y más información, así como la PEP 557 para la descripción oficial.

personalización del módulo de Atributos Los atributos

están por todas partes en Python! Mientras que los atributos de clase son, probablemente, el más famoso, en realidad atributos se pueden poner en esencia, cualquier cosa, incluyendo funciones y módulos. Varias de las características básicas de Python se implementan como atributos: la mayoría de la funcionalidad de la introspección, doc-strings, y espacios de nombres. Funciones dentro de un módulo están disponibles como atributos del módulo. Atributos

más a menudo se recuperan con la notación de puntos: thing.attribute. Sin embargo, también se puede obtener atributos que se denominan en tiempo de ejecución utilizando getattr ():

import random

random_attr = random.choice(("gammavariate", "lognormvariate", "normalvariate"))
random_func = getattr(random, random_attr)

print(f"A {random_attr} random value: {random_func(1, 1)}")

La ejecución de este código producirá algo como:

A gammavariate random value: 2.8017715125270618

Para las clases, llamando thing.attr buscará primero attr definido en cosa. Si no se encuentra, entonces la cosa especial método .__ getattr __ ( "attr") se llama. (Esta es una simplificación. Ver este artículo para más detalles.) El método .__ getattr __ () se puede utilizar para personalizar el acceso a los atributos de los objetos.

Hasta Python 3.7, la misma personalización no era fácilmente disponible para atributos del módulo. Sin embargo, PEP 562 introduce __getattr __ () en los módulos, junto con una función de () __dir correspondiente __. El __dir __ () función especial permite la personalización del resultado de la llamada dir () en un módulo.

El propio PEP da algunos ejemplos de cómo se pueden utilizar estas funciones, incluyendo la adición de avisos de obsolescencia a las funciones y la carga diferida de submódulos pesados. A continuación, vamos a construir un sistema simple plugin que permite a las funciones que se añadirán a un módulo dinámicamente. Este ejemplo se aprovecha de los paquetes de Python. Ver este artículo si necesita un repaso de los paquetes.

Crear un nuevo directorio, plugins y añada el siguiente código en un archivo, plugins / __ init__.py:

from importlib import import_module
from importlib import resources

PLUGINS = dict()

def register_plugin(func):
"""Decorator to register plug-ins"""
name = func.__name__
PLUGINS[name] = func
return func

def __getattr__(name):
"""Return a named plugin"""
try:
return PLUGINS[name]
except KeyError:
_import_plugins()
if name in PLUGINS:
return PLUGINS[name]
else:
raise AttributeError(
f"module {__name__!r} has no attribute {name!r}"
) from None

def __dir__():
"""List available plug-ins"""
_import_plugins()
return list(PLUGINS.keys())

def _import_plugins():
"""Import all resources to register plug-ins"""
for name in resources.contents(__name__):
if name.endswith(".py"):
import_module(f"{__name__}.{name[:-3]}")

Antes de ver lo que este código, agregue dos archivos más dentro del directorio de plugins. En primer lugar, vamos a ver plugins / plugin_1.py:

from . import register_plugin

@register_plugin
def hello_1():
print("Hello from Plugin 1")

A continuación, añadir un código similar en el archivo de plugins / plugin_2.py:

from . import register_plugin

@register_plugin
def hello_2():
print("Hello from Plugin 2")

@register_plugin
def goodbye():
print("Plugin 2 says goodbye")

Estos complementos se pueden utilizar ahora de la siguiente manera:

>>> import plugins
>>> plugins.hello_1()
Hello from Plugin 1

>>> dir(plugins)
['goodbye', 'hello_1', 'hello_2']

>>> plugins.goodbye()
Plugin 2 says goodbye

Esto no puede parecer todo lo revolucionario ( y es probable que no lo es), pero vamos a ver lo que realmente ocurrió aquí. Normalmente, para poder llamar plugins.hello_1 (), la función hello_1 () debe estar definido en un módulo o complementos importados de manera explícita en el interior __init__.py en un paquete de plugins. Aquí, no es ni!

En cambio, hello_1 () se define en un archivo arbitrario dentro del paquete de plugins, y hello_1 () se convierte en una parte del paquete de plugins mediante el registro de sí mismo usando el decorador @register_plugin.

La diferencia es sutil. En lugar del dictado paquete de las funciones que están disponibles, las funciones individuales se registran como parte del paquete. Esto le da una estructura simple que permite añadir funciones independientemente del resto del código sin tener que mantener una lista centralizada de las funciones que están disponibles.

nos dejó hacer una revisión rápida de lo __getattr __ () hace dentro de los plugins / __ código init__.py. Cuando solicitó plugins.hello_1 (), Python busca primero una función hello_1 () dentro del archivo / plugins __ init__.py. Como no existe tal función, Python llama __getattr __ ( "hello_1") en su lugar. Recuerde que el código fuente de la __getattr __ () Función:

def __getattr__(name):
"""Return a named plugin"""
try:
return PLUGINS[name] # 1) Try to return plugin
except KeyError:
_import_plugins() # 2) Import all plugins
if name in PLUGINS:
return PLUGINS[name] # 3) Try to return plugin again
else:
raise AttributeError( # 4) Raise error
f"module {__name__!r} has no attribute {name!r}"
) from None

__getattr __ () contiene los siguientes pasos. Los números de la siguiente lista corresponden a los comentarios numerados en el código:

Cómo se poblaron los plugins Diccionario embargo? No parece que la _import_plugins (importaciones) de función todos los archivos de Python dentro del paquete de plugins, pero para tocar plugins:

def _import_plugins():
"""Import all resources to register plug-ins"""
for name in resources.contents(__name__):
if name.endswith(".py"):
import_module(f"{__name__}.{name[:-3]}")

no se olvide que cada función plug-in está decorado por el decorador @register_plugin. Este decorador se llama cuando los plugins son importados y es la que realmente poblar el diccionario de los plugins. Esto se puede ver si importa manualmente uno de los archivos del plugin:

>>> import plugins
>>> plugins.PLUGINS
{}

>>> import plugins.plugin_1
>>> plugins.PLUGINS
{'hello_1': }

Continuando con el ejemplo, nota que llamar dir () en el módulo también importa los plugins restantes: dir

>>> dir(plugins)
['goodbye', 'hello_1', 'hello_2']

>>> plugins.PLUGINS
{'hello_1': ,
'hello_2': ,
'goodbye': }

() usualmente se enumeran todos los atributos sobre un objeto . Normalmente, el uso de dir () en un módulo provoca algo como esto:

>>> import plugins
>>> dir(plugins)
['PLUGINS', '__builtins__', '__cached__', '__doc__',
'__file__', '__getattr__', '__loader__', '__name__',
'__package__', '__path__', '__spec__', '_import_plugins',
'import_module', 'register_plugin', 'resources']

Si bien esto puede ser útil la información, estamos más interesados ​​en exponer los plugins disponibles. En Python 3.7, se puede personalizar el resultado de la llamada dir () en un módulo añadiendo una __dir __ () función especial. Para plugins / __ init__.py, esta función primero se asegura se han importado todos los plugins y luego enumera sus nombres:

def __dir__():
"""List available plug-ins"""
_import_plugins()
return list(PLUGINS.keys())

Antes de abandonar este ejemplo, tenga en cuenta que también se utiliza otra nueva característica interesante de Python 3.7. Para importar todos los módulos dentro del directorio de plugins, se utilizó el nuevo módulo importlib.resources. Este módulo permite el acceso a archivos y recursos dentro de los módulos y paquetes sin necesidad de __FILE__ hacks (que no siempre funcionan) o pkg_resources (que es lento). Otras características de importlib.resources se destacarán más adelante.

Typing Mejoras

consejos y anotaciones de tipo han estado en constante desarrollo a través de los Python 3 series de lanzamientos. sistema de tipificación de Python es ahora bastante estable. Aún así, Python 3.7 trae algunas mejoras en la tabla: mejor rendimiento, soporte de núcleo, y referencias hacia delante.

Python no hace ninguna comprobación de tipos en tiempo de ejecución (a menos que utilice explícitamente como paquetes de cumplir). Por lo tanto, la adición de consejos a su código de tipo no debería afectar a su rendimiento.

Por desgracia, esto no es del todo cierto ya que la mayoría indirectas de tipo necesitan el módulo de escribir. El módulo de escribir es uno de los módulos más lentos en la biblioteca estándar. PEP 560 añade un poco de apoyo básico para escribir en Python 3.7, lo que acelera significativamente el módulo de escribir. Los detalles de esto son, en general, no es necesario conocer. Simplemente volver magra y disfrutar del mayor rendimiento.

Mientras sistema de tipos de Python es bastante expresiva, una cuestión que causa un poco de dolor es referencias hacia delante. indirectas o de tipo más general anotaciones se evalúan mientras que el módulo ha sido importada. Por lo tanto, todos los nombres tienen que estar definidos antes de ser utilizados. La siguiente no es posible:

class Tree:
def __init__(self, left: Tree, right: Tree) -> None:
self.left = left
self.right = right

ejecutar el código plantea una NameError porque la clase de árbol sin embargo, no es (completamente) definido en la definición del método .__ init __ ():

Traceback (most recent call last):
File "tree.py", line 1, in
class Tree:
File "tree.py", line 2, in Tree
def __init__(self, left: Tree, right: Tree) -> None:
NameError: name 'Tree' is not defined

Para superar esto, se habría necesitado para escribir "árbol" como un literal de cadena en lugar:

class Tree:
def __init__(self, left: "Tree", right: "Tree") -> None:
self.left = left
self.right = right

Ver PEP 484 para la discusión original.

En un futuro Python 4.0, se permitirá que estos llamados referencias hacia delante. Esto será manejado por no evaluar las anotaciones hasta que se les pide explícitamente. PEP 563 se describen los detalles de esta propuesta. En Python 3.7, referencias hacia delante ya están disponibles como una importación __future__. Ahora puede escribir el siguiente:

from __future__ import annotations

class Tree:
def __init__(self, left: Tree, right: Tree) -> None:
self.left = left
self.right = right

Tenga en cuenta que además de evitar la sintaxis algo torpe "árbol", la evaluación pospuesta de anotaciones también acelerará su código, desde consejos de tipo no se ejecutan. referencias hacia delante ya son soportados por mypy.

Por el momento, el uso más común de las anotaciones es de tipo insinuando. Sin embargo, usted tiene acceso completo a las anotaciones en tiempo de ejecución y puede utilizarlos como mejor le parezca. Si usted está manejando anotaciones directamente, es necesario para hacer frente a las posibles referencias hacia delante de forma explícita.

Vamos a crear algunos ejemplos que muestran cierto tontas cuando se evalúan las anotaciones. Primero lo hacemos al viejo estilo, por lo que las anotaciones se evalúan en tiempo de importación. Vamos anno.py contienen el código siguiente:

def greet(name: print("Now!")):
print(f"Hello {name}")

Tenga en cuenta que la anotación del nombre es de impresión (). Esto es sólo para ver exactamente cuando se evalúa la anotación. Importar el nuevo módulo:

>>> import anno
Now!

>>> anno.greet.__annotations__
{'name': None}

>>> anno.greet("Alice")
Hello Alice

Como se puede ver, la anotación se evaluó en el momento de la importación. Tenga en cuenta que hasta extremos nombre anotados con ninguno porque ese es el valor de retorno de impresión ().

Añadir la importación __future__ para permitir la evaluación pospuesta de anotaciones:

from __future__ import annotations

def greet(name: print("Now!")):
print(f"Hello {name}")

importar este código actualizado no evaluará la anotación:

>>> import anno

>>> anno.greet.__annotations__
{'name': "print('Now!')"}

>>> anno.greet("Marty")
Hello Marty

Tenga en cuenta que ahora! Nunca se imprime y la anotación se mantiene como un literal de cadena en el diccionario __annotations__. Con el fin de evaluar la anotación, uso typing.get_type_hints () o eval ():

>>> import typing
>>> typing.get_type_hints(anno.greet)
Now!
{'name': }

>>> eval(anno.greet.__annotations__["name"])
Now!

>>> anno.greet.__annotations__
{'name': "print('Now!')"}

Observe que el diccionario __annotations__ no se actualiza, por lo que necesita para evaluar la anotación cada vez que lo utilice.

Timing Precision

En Python 3.7, el módulo de tiempo gana nuevas funciones como se describe en PEP 564. En particular, se añaden los siguientes seis funciones: clock_gettime_ns

  • (): devuelve el tiempo de un reloj especificada clock_settime_ns
  • (): Establece el tiempo de un reloj especificada monotonic_ns
  • (): devuelve la hora de un reloj relativo que no puede ir hacia atrás (por ejemplo, debido al horario de verano) perf_counter_ns
  • (): Devuelve el valor de un contador de rendimiento -un reloj diseñado específicamente para medir intervalos cortos process_time_ns
  • (): Devuelve la suma de la hora del sistema y la CPU de usuario del proceso actual (sin incluir el tiempo de sueño) time_ns
  • (): Devuelve el número de nanosegundos desde el 1 de enero de 1970

En un sentido, no hay ninguna funcionalidad nueva añadido. Cada función es similar a una función ya existente sin el sufijo _ns. La diferencia es que las nuevas funciones devuelven un número de nanosegundos como un int en lugar de un número de segundos como un flotador.

Para la mayoría de aplicaciones, la diferencia entre estas nuevas funciones nanosegundos y su homólogo de edad no será apreciable. Sin embargo, las nuevas funciones son más fáciles de razonar acerca de porque se basan en int en lugar de flotador. números de punto flotante son por naturaleza inexacta:

>>> 0.1 + 0.1 + 0.1
0.30000000000000004

>>> 0.1 + 0.1 + 0.1 == 0.3
False

Esto no es un problema con Python, sino más bien una consecuencia de las computadoras que necesitan para representar números decimales infinitos utilizando un número finito de bits.

A Python flotador sigue el estándar IEEE 754 y utiliza 53 bits significativos. El resultado es que cualquier tiempo mayor de aproximadamente 104 días (2⁵³ o aproximadamente 9 billones de nanosegundos) no se puede expresar como un flotador con una precisión de nanosegundos. Por el contrario, un int de Python es ilimitado, por lo que un número entero de nanosegundos siempre tendrá una precisión de nanosegundos independiente del valor del tiempo.

A modo de ejemplo, time.time () devuelve el número de segundos desde el 1 de enero de 1970. Este número ya es bastante grande, por lo que la precisión de este número es a nivel de microsegundos. Esta función es la que muestra la mayor mejora en su versión _ns. La resolución de time.time_ns () es de aproximadamente 3 veces mejor que la de time.time ().

¿Qué es un nanosegundo por cierto? Técnicamente, es una mil millonésima parte de un segundo, o 1e-9 segundos, si lo prefiere la notación científica. Estos son sólo números y aunque realmente no proporcionan ninguna intuición. Para una mejor ayuda visual, consulte maravillosa demostración del nanosegundo de Grace Hopper.

Como acotación al margen, si usted necesita trabajar con datetimes con una precisión de nanosegundos, la fecha y hora de la biblioteca estándar no es suficiente. Explícitamente sólo se ocupa de microsegundos:

>>> from datetime import datetime, timedelta
>>> datetime(2018, 6, 27) + timedelta(seconds=1e-6)
datetime.datetime(2018, 6, 27, 0, 0, 0, 1)

>>> datetime(2018, 6, 27) + timedelta(seconds=1e-9)
datetime.datetime(2018, 6, 27, 0, 0)

su lugar, se puede utilizar el proyecto astropy. Su paquete de astropy.time representa datetimes utilizando dos objetos flotantes que garantiza “precisión sub-nanosegundo sobre los tiempos que abarcan la edad del universo.”

>>> from astropy.time import Time, TimeDelta
>>> Time("2018-06-27")

>>> t = Time("2018-06-27") + TimeDelta(1e-9, format="sec")
>>> (t - Time("2018-06-27")).sec
9.976020010071807e-10

La última versión de astropy está disponible en Python 3.5 y posterior.

Otras características Pretty Cool

Hasta el momento, se han visto la noticia de primera plana con respecto a lo que hay de nuevo en Python 3.7. Sin embargo, hay muchos otros cambios que son también muy bueno. En esta sección, vamos a examinar brevemente algunos de ellos.

La Orden de Diccionarios está garantizado aplicación

El CPython de Python 3.6 ha ordenado a los diccionarios. (PyPy también tiene este). Esto significa que los artículos en los diccionarios se repiten a lo largo de la misma orden en que se insertan. El primer ejemplo es el uso de Python 3.5, y el segundo está utilizando Python 3.6:

>>> {"one": 1, "two": 2, "three": 3} # Python <= 3.5 {'three': 3, 'one': 1, 'two': 2} >>> {"one": 1, "two": 2, "three": 3} # Python >= 3.6
{'one': 1, 'two': 2, 'three': 3}

En Python 3.6, este ordenamiento fue tan sólo un buen consecuencia de que la aplicación de dict. En Python 3.7, sin embargo, los diccionarios preservar su orden de inserción es parte de la especificación del lenguaje. Como tal, ahora puede ser invocado en proyectos que sólo un apoyo Python > = 3,7 (o CPython > = 3,6).

“asíncrono” y son palabras clave

Python 3.5 corrutinas introducidas “se espera la” con asíncrono y la sintaxis esperan. Para evitar problemas de compatibilidad hacia atrás, asíncrono y esperar que no se han añadido a la lista de palabras clave reservadas. En otras palabras, todavía era posible definir variables o funciones con nombre asíncrono y esperar.

En Python 3.7, esto ya no es posible:

>>> async = 1
File "", line 1
async = 1
^
SyntaxError: invalid syntax

>>> def await():
File "", line 1
def await():
^
SyntaxError: invalid syntax

“asyncio” estiramiento facial

El asyncio biblioteca estándar se introdujo originalmente en Python 3.4 para manejar la concurrencia de una manera moderna, utilizando bucles de eventos, corrutinas y futuros. Aquí es una introducción suave.

En Python 3.7, el módulo asyncio está recibiendo un importante lavado de cara, incluyendo muchas nuevas funciones, el apoyo a las variables de contexto (véase más adelante), y mejoras de rendimiento. De nota particular está asyncio.run (), que simplifica las llamadas co-rutinas de código síncrono. Usando asyncio.run (), que no es necesario crear explícitamente el bucle de eventos. Un programa Hello World asíncrono se puede escribir:

import asyncio

async def hello_world():
print("Hello World!")

asyncio.run(hello_world())

variables de contexto las variables de contexto

son variables que pueden tener valores diferentes en función de su contexto. Son similares a los de subproceso local de almacenamiento en la que cada hilo de ejecución puede tener un valor diferente para una variable. Sin embargo, con las variables de contexto, puede haber varios contextos en un hilo de ejecución. El caso de uso principal de las variables de contexto es hacer el seguimiento de las variables en las tareas asíncronas simultáneas.

El ejemplo siguiente construye tres contextos, cada uno con su propio valor para el nombre de valor. La función greet () es posterior capaz de utilizar el valor de nombre dentro de cada contexto:

import contextvars

name = contextvars.ContextVar("name")
contexts = list()

def greet():
print(f"Hello {name.get()}")

# Construct contexts and set the context variable name
for first_name in ["Steve", "Dina", "Harry"]:
ctx = contextvars.copy_context()
ctx.run(name.set, first_name)
contexts.append(ctx)

# Run greet function inside each context
for ctx in reversed(contexts):
ctx.run(greet)

La ejecución de este script de Steve saluda, Dina, y Harry en el orden inverso:

$ python3.7 context_demo.py
Hello Harry
Hello Dina
Hello Steve

Importación de archivos de datos con “importlib.resources”

Uno desafiar al empaquetar un proyecto de Python es decidir qué hacer con los recursos del proyecto como archivos de datos necesarios para el proyecto. Unas pocas opciones han sido comúnmente usados:

  • duro-código de una ruta de acceso al archivo de datos.
  • Ponga el archivo de datos dentro del paquete y localizar usando __file__.
  • Uso setuptools.pkg_resources para acceder al recurso de archivo de datos.

Cada uno de ellos tiene sus defectos. La primera opción no es portátil. Usando __file__ es más portátil, pero si se instala el proyecto Python podría terminar dentro de una postal y no tener un atributo __file__. La tercera opción resuelve este problema, pero por desgracia es muy lento.

Una mejor solución es el nuevo módulo de importlib.resources en la biblioteca estándar. Utiliza funcionalidad de importación existente de Python para también archivos de datos de importación. Suponga que tiene un recurso dentro de un paquete de Python como esto:

data/

├── alice_in_wonderland.txt
└── __init__.py

Tenga en cuenta que las necesidades de datos para ser un paquete de Python. Es decir, las necesidades directorio que contiene un archivo __init__.py (que puede estar vacía). A continuación, puede leer el archivo alice_in_wonderland.txt de la siguiente manera:

>>> from importlib import resources
>>> with resources.open_text("data", "alice_in_wonderland.txt") as fid:
... alice = fid.readlines()
...
>>> print("".join(alice[:7]))
CHAPTER I. Down the Rabbit-Hole

Alice was beginning to get very tired of sitting by her sister on the
bank, and of having nothing to do: once or twice she had peeped into the
book her sister was reading, but it had no pictures or conversations in
it, "and what is the use of a book," thought Alice "without pictures or
conversations?"

Una función similar resources.open_binary () está disponible para abrir archivos en modo binario. En los “plugins como atributos del módulo” anteriores ejemplo, utilizamos importlib.resources para descubrir los plugins disponibles utilizando resources.contents (). Ver charla PyCon 2018 de Barry Varsovia para más información.

Es posible utilizar importlib.resources en Python 2.7 y Python 3.4+ a través de un backport. Una guía sobre la migración de pkg_resources a importlib.resources está disponible.

trucos Desarrollador

Python 3.7 ha añadido varias características destinadas a usted como desarrollador. Ya has visto el nuevo punto de interrupción () incorporado. Además, algunas opciones nuevas de línea de comandos -X se han añadido a la intérprete de Python.

Usted puede obtener una idea de la cantidad de tiempo que las importaciones en su script toma, utilizando importtime -X:

$ python3.7 -X importtime my_script.py
import time: self [us] | cumulative | imported package
import time: 2607 | 2607 | _frozen_importlib_external
...
import time: 844 | 28866 | importlib.resources
import time: 404 | 30434 | plugins

Los espectáculos de columna acumulativos del tiempo acumulado de las importaciones (en microsegundos). En este ejemplo, los plugins importadores tomaron aproximadamente 0,03 segundos, la mayoría de los cuales se gastaron importar importlib.resources. La columna de auto muestra el tiempo de importación excluyendo las importaciones anidados. Ahora

Puede utilizar -X dev para activar el “modo de desarrollo.” El modo de desarrollo añadirá ciertas características de depuración y comprobaciones de tiempo de ejecución que se consideran demasiado lento para ser activado por defecto. Estos incluyen permitiendo faulthandler para mostrar un rastreo de los accidentes graves, así como más advertencias y ganchos de depuración.

Finalmente, utf8 -X activa el modo UTF-8. (Ver PEP 540.) En este modo, se utilizará UTF-8 para el texto que codifica independientemente de la localización actual.

optimizaciones

Cada nueva versión de Python viene con un conjunto de optimizaciones. En Python 3.7, hay algunas importantes aceleraciones, incluyendo:

  • Hay menos sobrecarga para pedir muchos métodos en la biblioteca estándar. llamadas
  • método son hasta un 20% más rápido en general.
  • El tiempo de inicio de sí mismo Python se reduce en un 10-30%.
  • tipificación Importación es 7 veces más rápido.

Además, se incluyen muchas optimizaciones más especializados. Ver esta lista para obtener una descripción detallada.

El resultado de todas estas optimizaciones es que Python 3.7 es rápida. Es simplemente la versión más rápida de CPython lanzado hasta ahora.

Por lo tanto, debo actualizar? inicio de

Vamos con la respuesta simple. Si desea probar cualquiera de las nuevas características que hemos visto aquí, entonces sí es necesario para poder utilizar Python 3.7. El uso de herramientas tales como pyenv o Anaconda hace que sea fácil tener varias versiones de Python instalado al lado del otro. No hay ningún inconveniente para la instalación de Python 3.7 y tratar a cabo.

Ahora, para las cuestiones más complicadas. Debe actualizar su entorno de producción a Python 3.7? En caso de hacer depender su propio proyecto en Python 3.7 para tomar ventaja de las nuevas características?

Con la advertencia obvio que siempre se debe hacer pruebas exhaustivas antes de actualizar el entorno de producción, hay muy pocas cosas en Python 3.7 que va a romper el código anterior (asíncrono y esperar convertirse en palabras clave es un ejemplo sin embargo). Si ya está utilizando un moderno Python, la actualización a 3.7 debe ser bastante suave. Si quieres ser un poco más conservador, es posible que desee esperar a que el lanzamiento de la primera versión de mantenimiento en Python 3.7.1-tentativamente espera alguna vez en julio de 2018.

el argumento de que usted debe hacer su proyecto 3.7 sólo es más difícil. Muchas de las nuevas características de Python 3.7 o bien están disponibles como backports a Python 3.6 (clases de datos, importlib.resources) o conveniencias (inicio más rápido y más fácil, las llamadas a métodos de depuración, y -X opciones). Este último, se puede aprovechar mediante la ejecución de Python 3.7 a sí mismo, manteniendo su código compatible con Python 3.6 (o bajar).

Las grandes características que cierran su código a Python 3.7 son __getattr __ () en módulos, referencias hacia delante en consejos de tipo, y las funciones de tiempo de nanosegundos. Si realmente necesita ninguno de estos, usted debe seguir adelante y chocar sus necesidades. De lo contrario, el proyecto probablemente será más útil a los demás si se puede ejecutar en Python 3.6 por un tiempo más largo.

Véase la migración a Python 3.7 Guía para los detalles a tener en cuenta cuando se actualiza.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *