Skip to content

Nim for Python Programmers PT_BR

Nilo Menezes edited this page Feb 4, 2023 · 1 revision

Índice

Comparação Objetos [Eu tenho que conhecer C?](#do -eu tenho que saber-c) [Tuplas nomeadas](#named -tuple-1)
Variáveis self.__init__() Strings, F-Strings Listas
[Nomeando de variáveis](#variable -naming) [Espaçamento consistente](#consistent -spacing) Escopo [Argumentos mutáveis](#mutable -arguments)
Imports Intervalos [Operações com cadeias de caracteres](#string -ops) List Comprehensions
try/import/except [Verificações estática de limites](#static -bounds-check) [Coalescência nula](#null -coalescência) [with Gerenciador de Contextos](#with -context-manager)
Matrizes Fatias Tuplas [Dict Compreensions](#Dict -Compreensões)
[Set Comprehensions](#Set -Compreensões) [Lendo e gravando arquivos](#Reading -and-writing-files) Decoradores Lambdas
Conjuntos JSON [Map e filter](#map —filter) [Indentação opcional](#Optional -Indentação)
Dicionários CamelCase DocStrings [Importar arquivos Nim em Python](#Import -Nim-files-on-Python)
[Operadores ternários](#Ternary -operadores) Testes unitários [def x proc/func](#def -vs-procfunc) [Autoexecução do módulo principal](#Self -Execução do módulo principal)
[Sintaxe Python para Nim](#Python -Syntax-for-NIM) [Publicar no PYPI](#Publish para PYPI) [Compilação silenciosa](#Silent -Compilação) [Ajuda do compilador](#Compiler -Help)
[Modos de construção](#Build -Modes) ABC - Classes básicas abstratas Decoradores WebAssembly
Modelos Nim executando interpretado Nim no navegador Equivalentes da biblioteca padrão
Assíncrono Instale o Nim do PIP Folha de dicas em PDF Funções Arrow
Como compartilhar variáveis entre funções? [Alterar permissões do arquivo](#Change -File-Permissions) [Alterar pasta temporariamente](#Temporarily -Change-Folder) Pattern Matching - Correspondência de padrões
Melhores práticas [No local versus fora do lugar](#in -Place-vs-Out-Place) Executar no NodeJS [Expansão de código](#run -time-code-expansion)
Arduino, MicroPython, ESP32, RTOS grátis Codificação ao vivo, FoxDot, SuperCollider Nim instalável por PIP incorporado em Python Instale pacotes Nim do PIP

Comparação

Característica 🐍 Python 👑 Nim
Modelo de execução Máquina virtual (interpretador) Código de máquina via C/C++ (compilador)
Escrito usando C (CPython) Nim
Licença Licença da Python Software Foundation MIT
Versão (principal) 3.x 1.x
Metaprogramação ✔️ metaclass, exec, eval, ast
(expansão de código em tempo de execução) ✔️ modelo, macros
(expansão do código em tempo de compilação)
Gerenciamento de memória Coletor de lixo Gerenciamento de memória multiparadigma
(coletores de lixo, ARC/ORC,
manual)
Tipagem Dinâmica, Duck Typing Estática
Tipos dependentes ✔️ Suporte parcial
Genéricos Duck Typing ✔️
tipos
int8/16/32/64 ✔️
tipos uint8/16/32/64 ✔️
tipos float32/float64 ✔️
Tipos de caractere ✔️
Tipos de subfaixa ✔️ ✔️
Tipos de enum ✔️ ✔️
Bigints (tamanho arbitrário) ✔️ ✔️ jsbigints,
#14696
Maior número inteiro embutido Desconhecido, limitado pela memória livre 18_446_744_073_709_551_615 para o tipo uint64
Matrizes ✔️ ✔️
Inferência de tipo Duck typing ✔️
Closures ✔️ ✔️
Sobrecarga do operador ✔️ ✔️ em qualquer tipo
Operadores personalizados ✔️
Orientado a objetos ✔️ ✔️
Métodos ✔️ ✔️
Exceções ✔️ ✔️
Funções anônimas ✔️ várias linhas, expressão única ✔️ várias linhas, várias expressões
List comprehensions ✔️ ✔️
Dict comprehensions ✔️ ✔️
Set comprehensions ✔️ ✔️
Compreensões personalizadas de objetos ✔️ expressão geradora ✔️
Pattern Matching embutido ✔️ A partir do Python 3.10 ✔️
Imutabilidade dos tipos Tipos básicos (número, string, bool), tuple, frozenset ✔️
Imutabilidade das variáveis ✔️
Imutabilidade dos argumentos da função Dependendo do tipo Imutável
Literais de string formatados ✔️ f-strings ✔️ strformat
FFI ✔️ ctypes, API de extensão C (Cython via pip) ✔️ C, C++, Objective C, JS (dependendo do backend usado)
Assíncrono ✔️ ✔️
Tópicos ✔️ GIL - Bloqueio de intérprete global ✔️
Regex ✔️ compatível com Perl ✔️ compatível com Perl
Comentários da documentação ✔️ cadeias de texto simples com várias linhas (reStructuredText
via Sphinx) ✔️ reStructuredText/Markdown
Publicação de pacotes ✔️ não embutido, requer twine ✔️ embutido, nimble
Gerenciador de pacotes ✔️ pip ✔️ nimble
Formatador automático de código ✔️ black e outros via pip ✔️ nimpretty embutido, nimlint
Extensões de arquivo .py, .pyw, .pyc, .pyd, .so .nim, .nims
Formato de representação intermediária temporária (IR) .pyc (código de bytes da VM do CPython) C, C++, Objective C (LLVM IR via nlvm)
Usa #! shebang em arquivos ✔️ ✔️ nimr, nimcr
REPL ✔️ [inim](https://github.com/inim-repl/INim#inim-interactive-nim-shell —),
Nim4Colab
Indentação Tabulações e espaços, uniformes por bloco de código, 4 espaços por convenção Somente espaços, uniforme por bloco de código, 2 espaços por convenção

Notas:

  • Sabe-se que as funções anônimas do Python (lambdas) são lentas em comparação com as funções normais.
  • O Python Regex afirma ser compatível com PCRE, mas na prática os PCRE Regexes podem não funcionar.
  • As funções anônimas “de várias linhas” do Python podem exigir o uso de ; e o Linters/IDE pode reclamar disso.

Variáveis

A criação de uma nova variável usa var ou let ou const. Nim tem imutabilidade e execução de funções em tempo de compilação. Você pode atribuir funções às variáveis.

Característica const let var
Tempo de execução NÃO ✔️ SIM ✔️ SIM
Tempo de compilação ✔️ SIM NÃO NÃO
Imutável ✔️ SIM ✔️ SIM NÃO
Inicializado automaticamente ✔️ SIM ✔️ SIM ✔️ SIM
Reatribuível NÃO NÃO ✔️ SIM
Requer atribuição ✔️ SIM ✔️ SIM NÃO
Pode ser global ✔️ SIM ✔️ SIM ✔️ SIM

Para usuários avançados, é possível ignorar a inicialização automática de variáveis.

Nomeação de variáveis

As variáveis podem ter várias linhas sem “escapar” delas ou usar parênteses. Isso é útil para linhas longas e operadores ternários longos. Exemplo mínimo:

variable = 666 +  \
  420 *  \
  42 -   \
  9           

assert variable == 18297

⬆️ Python ⬆️          ⬇️ Nim ⬇️

var variable = 666 +
  420 *
  42 -
  9

assert variable == 18297

Isso também funciona com chamadas de função:

import std/strutils

var variable = "  12345  "
  .strip
  .parseInt

assert variable == 12345

Você pode usar sublinhados, novas linhas (new lines) e espaços em branco nos nomes das variáveis:

let `this must  be  
     positive`: Positive = 42

assert this_must_be_positive == 42

const `this is my nice named variable` = 42

Você pode usar palavras-chave reservadas como nomes de variáveis.

Não há problema em usar var enquanto aprende Nim ou para prototipagem rápida, embora seja muito melhor aprender a diferença entre diferentes declarações de variáveis.

Espaçamento consistente

Os espaços devem ser consistentes em seu código, principalmente em torno dos operadores:

echo 2 - 1 # OK
echo 2-1   # OK

Espaços inconsistentes incorretos:

echo 2 -1 # Error
#      ^ parses as "-1"

Omitir espaços em seu código não afeta o desempenho.

Todos os operadores são funções no Nim.

Escopo

  • Escopo: “vazamentos”, “bugs”, “falhas”, etc.
for x in range(0, 9):
  if x == 6:
    print(x)

print(x)

Saída:

6
8  # Leak!

⬆️ Python ⬆️          ⬇️ Nim ⬇️

for x in 0..9:
  if x == 6:
    echo x

echo x

Saída:

Error: undeclared identifier: 'x'

Observe que, no exemplo acima, usamos um simples int, então o problema pode não parecer grave. Mas se x tivesse alguns gigabytes de RAM, ele “vazaria” do loop for para o resto do escopo externo ou principal, em vez de ser recuperado. Nim evita esse problema.

Outro exemplo:

x = 0
y = 0

def example():
  x = 1
  y = 1
  class C:
    nonlocal x, y
    assert x == 1 and y == 1
    x = 2

example()

⬆️ Python ⬆️          ⬇️ Nim ⬇️

var x = 0
var y = 0

proc example() =
  var x = 1
  var y = 1
  type C = object
  assert x == 1 and y == 1
  x = 2

example()

Outro exemplo:

x = 0
y = 0

def example():
  x = 1
  y = 1
  class C:
    nonlocal x, y
    assert x == 1 and y == 1
    x = 2
    try:
      raise
    except Exception as _:
      pass

example()

⬆️ Python ⬆️          ⬇️ Nim ⬇️

var x = 0
var y = 0

proc example() =
  var x = 1
  var y = 1
  type C = object
  assert x == 1 and y == 1
  x = 2
  try:
    raise
  except Exception as y:
    discard

example()

Condicionais booleanos

  • Comparações booleanas “bugs”, “falhas”, etc.
assert True == not False

Falha com o erro:

SyntaxError: invalid syntax.

⬆️ Python ⬆️          ⬇️ Nim ⬇️

O exemplo do Nim é compilado e executado sem incidentes; a precedência do operador é resolvida corretamente:

assert true == not false

Outro exemplo:

assert False + 1
assert not True - 1

Isso é executado porque bool é um subtipo de int em Python, portanto, ele suporta as mesmas operações matemáticas. Em Nim, esse não é o caso:

⬆️ Python ⬆️          ⬇️ Nim ⬇️

assert false + 1
assert not true - 1

Não compila:

Error: type mismatch: got <bool, int>

bloco

block cria explicitamente um novo escopo, sem a sobrecarga de uma função. Ele pode ter um “nome” sem que o nome polua o namespace local e pode ser interrompido em qualquer lugar sem exigir retornar.

block também pode ser usado com var, let e const.

Imagine que você precise sair de um if aninhado, sem executar nenhum outro código de outros blocos if e else. Você pode fazer:

print("Before")

# this is a function, has overhead, pollutes namespace, must return to interrupt, etc.
def example():
  if True:
    print("Inside if true")
    if 42 > 0:
      print("Inside if 42 > 0")
      if 'z' > 'a':
        print("Inside if z > a")
        return  # Must return to interrupt
        if 3.14 > 0.0:
          print("Inside if 3.14 > 0.0")
      else:
        print("else of z > a")
    else:
      print("else of 42 > 0")
  else:
    print("else of true")

example()  # example in namespace
print("After")

⬆️ Python ⬆️          ⬇️ Nim ⬇️

echo "Before"

block example:  # Creates a new explicit named scope. This is not a function; there is no overhead.
  if true:
    echo "Inside if true"
    if 42 > 0:
      echo "Inside if 42 > 0"
      if 'z' > 'a':
        echo "Inside if z > a"
        break example  # Gets out of block example.
        if 3.14 > 0.0:
          echo "Inside if 3.14 > 0.0"
      else:
        echo "else of z > a"
    else:
      echo "else of 42 > 0"
  else:
    echo "else of true"

# No function call. "example" is not polluting the local namespace.
echo "After"

Argumentos mutáveis

def example(argument = [0]):
  argument.append(42)
  return argument

print(example())
print(example())
print(example())

Saída:

[0, 42]
[0, 42, 42]
[0, 42, 42, 42]

⬆️ Python ⬆️          ⬇️ Nim ⬇️

func example(argument = @[0]): auto =
  argument.add 42
  return argument

echo example()
echo example()
echo example()

Saída:

Error: type mismatch: got <seq[int], int literal(42)>

but expected one of: 
proc add[T](x: var seq[T]; y: sink T)
  first type mismatch at position: 1
  required type for x: var seq[T]
  but expression 'argument' is immutable, not 'var'

Importações

Importar 🐍 Python 👑 Nim
Apenas um símbolo, use não qualificado from math import sin from std/math import sin
Todos os símbolos, use sem qualificação from math import * **import std/math (recomendado) **
Todos os símbolos, use totalmente qualificados **import math (recomendado) ** from std/math import nil
“importar as” outro nome import math as batata import std/math as batata
Ambos os itens acima ao mesmo tempo from std/math as m import nil
Todos os símbolos, exceto um, usam sem qualificação import std/math except sin
Todos os símbolos, exceto vários, usam sem qualificação import std/math except sin, tan, PI
Incluir outro módulo neste módulo incluir algum módulo

**Seus módulos e tipos não vão colidir! , mesmo que você tenha tipos chamados de módulos, relaxe e continue codificando... **

No Nim, import std/math importa todos os símbolos do módulo math (sin, cos, etc) para que eles possam ser usados sem qualificação. O equivalente em Python é from math import *.

Se você preferir não importar todos os símbolos e sempre usar nomes qualificados, o código Nim é from std/math import nil. Então você pode chamar math.sin () , math.cos (), etc. O equivalente em Python é import math.

Geralmente, é seguro importar todos os nomes no Nim porque o compilador não compilará nenhuma função não utilizada (portanto, não há sobrecarga). Além disso, como o Nim é estaticamente tipado, ele geralmente pode distinguir entre as duas funções importadas com os mesmos nomes com base nos tipos de argumentos com os quais são chamadas. Nos raros casos em que os tipos são iguais, você ainda pode qualificar totalmente o nome para desambiguar.

O prefixo std/ impõe que o módulo seja importado da biblioteca padrão. Se um pacote Nimble tiver um módulo com o mesmo nome, o compilador pode resolver a ambiguidade e isso está explícito no código.

Módulos locais e módulos Nimble não precisam do prefixo std/.

Python e Nim compartilham essas declarações de importação:

# Python and Nim
import foo, bar, baz

import foo
import bar
import baz

Sintaxes alternativas:

# Python
import foo, \
       bar, \
       baz
# Nim
import foo,
       bar,
       baz

# Useful for small diffs when adding/removing imports
import
  foo,
  bar,
  baz

import 
  foo, bar, baz,
  more, imports

A variante com uma instrução import por linha é comum em Python e Nim, mas em Nim a forma import foo, bar, baz também é vista com frequência.

Mais exemplos:

## This is documentation for the module.
#  This is a comment.
include prelude
import std/sugar as stevia
from std/math import nil
from std/with as what import nil

Programaticamente

__import__("math")

⬆️ Python ⬆️          ⬇️ Nim ⬇️

template imports(s) = import s
imports math

Código sem importações

Às vezes, na natureza, você pode ver exemplos de código ou arquivos sem as importações, mas eles de alguma forma funcionam de qualquer maneira. O motivo é que Nim pode usar import do comando compile ou de um arquivo .nims:

  • nim c --import:sugar file.nim
  • nim c --import:folder/mymodule file.nim
  • nim js --import:strutils --include:mymodule file.nim

Às vezes, projetos ou exemplos de código rápido usam isso para evitar a digitação. Graças ao Dead Code Elimination (Eliminação de código morto), se os símbolos importados não forem usados, eles não existirão na saída compilada.

Veja também:

Prelúdio

Às vezes, você pode achar que o Python tem mais símbolos disponíveis por padrão sem qualquer importação em comparação com o Nim. Para ter uma experiência semelhante de ter as estruturas básicas de dados e as importações mais comuns prontas para que você possa começar a codificar imediatamente, você pode usar prelude:

include prelude

echo now()             
echo getCurrentDir() 
echo "Hello $1".format("World")

prelude é um arquivo include que simplesmente importa módulos comuns para sua conveniência, para evitar a digitação. prelúdio também funciona para destinos em JavaScript.

De onde vêm os símbolos?

  • Se os símbolos não são qualificados, como você sabe de onde vêm os símbolos?

Dado que foo () é um símbolo:

  • Nota: você normalmente tem foo () , com suporte do UFCS.
  • Python: você normalmente tem object.foo () em vez de module.foo (), sem UFCS.

Normalmente, o Editor/IDE deve sugerir de onde vêm os símbolos, como em qualquer outra linguagem de programação:

!

O Nim vem integrado com o NimSuggest para integrações de editor/IDE.

Ao contrário do Python, o sistema de tipos de Nim tem todas as informações sobre todos os símbolos:

import std/macros
macro findSym(thing: typed) = echo thing.getType.lineInfo

findSym:
  echo  # Where echo comes from?.

echo vem de:

lib/system.nim(1929, 12)

Ao aprender Nim ou para prototipagem rápida, não há problema em usar os símbolos totalmente qualificados. Fazer isso não produz erros, mas o Nim idiomático evita isso.

Exportações

Em Python, todos os símbolos no módulo são visíveis e mutáveis dos módulos que o importam, incluindo símbolos que não devem ser usados ou alterados fora do módulo.

No Nim, tudo é privado por padrão e, portanto, não é visível em outros módulos. Para tornar os símbolos públicos e visíveis em outros módulos, você precisa usar o asterisco *:

let variable* = 42
const constant* = 0.0
proc someFunction*() = discard
template someTemplate*() = discard
type Platypus* = object
  fluffyness*: int

O asterisco não apenas torna o símbolo visível para o mundo exterior, mas também aparecerá na documentação gerada (nim doc). Quando você importa o módulo, o símbolo será automaticamente adicionado ao namespace, mas símbolos privados (não exportados) sem * não estarão visíveis. O asterisco é como uma dica visual para humanos. Você pode entender imediatamente quais símbolos fazem parte da “API pública” apenas examinando o código-fonte do módulo. O asterisco * é pronunciado como “estrela”.

Para obter mais informações, leia: https://narimiran.github.io/2019/07/01/nim-import.html

try/import/except

Em Python, os imports são uma operação em tempo de execução e podem falhar. É um padrão bastante comum que os imports dependentes da plataforma sejam colocadas dentro de um bloco try e uma alternativa ou fallback dentro do bloco except:

try:
    import module
except ImportError:
    import othermodule as module

try:
    from module import some_func
except ImportError:
    # Fallback implementation
    def somefunc():
        return some_value

O Nim resolve todas as importações em tempo de compilação, então algo como um ImportError não existe. Não há necessidade de lidar com erros de importação em tempo de execução.

Matrizes

As matrizes em Nim são de tamanho fixo, começam no índice 0 e devem conter elementos do mesmo tipo.

Ao passar uma matriz para uma função em Nim, o argumento é uma referência imutável. O Nim incluirá verificações em tempo de execução nos limites das matrizes.

Você pode usar um openarray para aceitar uma matriz de qualquer tamanho nos argumentos da função, e você pode usar low (seu_array) e high (seu_array) para consultar os limites da matriz.

Nim string é compatível com openArray [char] para evitar cópias desnecessárias para otimização, e char é compatível com int. Portanto, a manipulação de strings pode ser feita com a matemática no local de forma transparente. Uma função que usa openArray [char] aceita "abcd" e ['a', 'b', 'c', 'd'].

O conteúdo da matriz é sempre contíguo na memória, assim como as matrizes de matrizes.

Veja também:

Tamanhos dos tipos de dados

  • Qual é o tamanho dos diferentes tipos de dados?.
import std/json

type Foo = object            
type Bar = enum true, false  

# (Weird spacing intended)
assert sizeOf( Foo )        == 1
assert sizeOf( Bar )        == 1
assert sizeOf( bool )       == 1
assert sizeOf( {true} )     == 1
assert sizeOf( [true] )     == 1
assert sizeOf( (true) )     == 1
assert sizeOf( int8 )       == 1

assert sizeOf( {'k': 'v'} ) == 2
assert sizeOf( int16 )      == 2

assert sizeOf( int32 )      == 4
assert sizeOf( float32 )    == 4

assert sizeOf( int )        == 8
assert sizeOf( float )      == 8
assert sizeOf( @[true] )    == 8
assert sizeOf( %*{} )       == 8
assert sizeOf( pointer )    == 8

Essa é apenas uma aproximação para as primitivas vazias em 64 bits.

Objetos

Objetos em Nim se comportam de forma bem diferente das classes em Python. Os objetos suportam herança e OOP. As classes são denominadas tipos em Nim. Funções (procs) são funções flutuantes livres, não vinculadas a objetos (no entanto, você pode usá-las de uma forma muito semelhante ao Python). Você pode chamar uma função em objetos com a sintaxe object.function () , bem como function (object); estes são totalmente equivalentes. Nim não tem um eu implícito nem isso. É uma boa prática colocar todos os tipos no topo do arquivo, mas isso não é obrigatório.

Uma forma de imaginar isso é que procs ficam “grudados” nos tipos de seus argumentos em tempo de compilação e, em seguida, você pode usá-los em tempo de execução como se fossem classes e métodos do Python.

Do Python ao Nim, o mínimo possível exemplo:

class Kitten:
    """ Documentation Here """

    def purr(self):
        print("Purr Purr")

Kitten().purr()

⬆️ Python ⬆️          ⬇️ Nim ⬇️

type Kitten = object  ## Documentation Here
proc purr(self: Kitten) = echo "Purr Purr"
Kitten().purr()

Exemplo de herança mínima:

type Animal = object of RootObj
type Kitten = object of Animal
assert Kitten is Animal

Exemplos de orientação de objetos semelhantes aos do Python:

type Animal = ref object of RootObj ## Animal base object.
  age: int                          
  name: string                      ## Attributes of base object.

type Cat = ref object of Animal     ## Cat inherited object.
  playfulness: float                ## Attributes of inherited object.

func increase_age(self: Cat) =
  self.age.inc()                    # Cat object function, access and *modify* object.

var kitten = Cat(name: "Tom")       # Cat object instance.
kitten.increase_age()               # Cat object function used.

assert kitten.name == "Tom"         # Assert on Cat object.
assert kitten.age == 1

Exemplo de herança:

type
  LUCA        = ref object of RootObj
  Archea      = ref object of LUCA
  Prokaryota  = ref object of Archea
  Eukaryota   = ref object of Prokaryota
  Animalia    = ref object of Eukaryota
  Chordata    = ref object of Animalia
  Mammalia    = ref object of Chordata
  Primates    = ref object of Mammalia
  Haplorhini  = ref object of Primates
  Simiiformes = ref object of Haplorhini
  Hominidae   = ref object of Simiiformes
  Homininae   = ref object of Hominidae
  Hominini    = ref object of Homininae
  Homo        = ref object of Hominini
  Homosapiens = ref object of Homo

assert Homosapiens() is LUCA
assert LUCA() isnot Homosapiens
assert sizeOf(Homosapiens) == sizeOf(LUCA)
let human = Homosapiens()
assert human is Homosapiens

Veja também:

self.__init__()

Depois do exemplo do Cat, você provavelmente está se perguntando como fazer def __init__ (self, arg) :.

Python __init__ () é Nim newObject () ou initObject () . Vamos fazer um __init__ () para o Cat:

type Cat = object                # Cat object.
  age: int                          
  name: string                   # Attributes of Cat object.

func initCat(age = 2): Cat =     # Cat.__init__(self, age=2)                    
  result.age = age               # self.age = age         
  result.name = "adopt_me"       # self.name = "adopt_me" 

var kitten = initCat()            # Cat object instance.

assert kitten.name == "adopt_me" # Assert on Cat object.
assert kitten.age == 2

A nomenclatura é uma convenção e uma boa prática. Quando você quiser init para Foo, basta criar newFoo () ou initFoo (). Como você pode notar, initCat é apenas uma função que retorna um Cat.

  • initFoo () para object.
  • newFoo () para ref object.

Leia a documentação para nomear coisas seguindo as convenções e as melhores práticas.

Valores padrão do atributo do objeto

O construtor de objetos também é a forma de definir valores padrão personalizados para os atributos de seus objetos:

type Cat = object
  age: int                 # AutoInitialized to 0
  name: string             # AutoInitialized to ""
  playfulness: float       # AutoInitialized to 0.0
  sleeping: bool           # AutoInitialized to false 
func initCat(): Cat =    
  result.age = 1           # Set default value to 1
  result.name = "Bastet"   # Set default value to "Bastet"
  result.playfulness = 9.0 # Set default value to 9.0
  result.sleeping = true   # Set default value to true

Uma estrutura mais completa para um programa básico pode ser algo como:

## Simple application to  do Foo with the Bar.

type
  Animal = ref object of RootObj 
    age: int                          
    name: string                     

  Cat = ref object of Animal
    playfulness: float   


func initCat(age = 2): Cat =   
  result.age = age            
  result.name = "adopt_me"     

func increase_age(self: Cat) =
  self.age.inc()               

proc main() =
  var kitten = Cat(name: "Tom")  
  kitten.increase_age()           

  assert kitten.name == "Tom"      
  assert kitten.age == 1


when isMainModule:
  main()

runnableExamples:
  echo "Optionally some documentation code examples here"
  assert 42 == 42

Expansão do código de execução

Objetos Python que internamente usam geração de código são muito, muito lentos, escalonando com o tamanho. Quanto mais você o usa, mais lento ele executa. dataclass, metaclasse, decoradores, etc. podem ser mais de 25 ~ 50x mais lentos do que uma classe normal. [Pathlib.path e seus métodos podem ser mais de 25 ~ 50x mais lentos do que um str] normal (https://youtu.be/tFrh9hKMS6Y) e anulam qualquer otimização, incluindo um arquivo .pyc. O Cython não tem CTFE, então isso não ajuda especificamente.

  • A expansão do código Nim é feita em tempo de compilação, tornando seu custo de geração de código zero em tempo de execução.

Por exemplo, você pode ver o resultado da expansão do código ARC durante a compilação usando —expandArc. É assim que o Nim faz o gerenciamento de memória em tempo de compilação (aproximação):

!

Veja também:

Unsafe Type Hints (dicas de tipagem inseguras)

As “type hints” do Python podem ser quase qualquer coisa e são executadas implicitamente em tempo de execução. Não é preciso dizer que isso pode ser muito inseguro:

$ cat example.py
class X: _: "print('PWNED')"  # os.system("rm -rf /folder ")
__import__("typing").get_type_hints(X)

$ python3 example.py

'PWNED'

$

Os tipos de Nim devem ser válidos; os tipos são verificados no momento da compilação:

$ cat example.nim
type X = object
  a: "echo('PWNED')"
echo X()

$ nim r example.nim    # Will not compile.

Error: type expected, but got: "echo('PWNED')"

$

Outro exemplo

$ cat example.nim
var example: "echo('PWNED')"
echo example

$ nim r example.nim    # Will not compile.

Error: type expected, but got: "echo('PWNED')"

$

Por valor x por referência

  • Nim transmite dados “por valor” ou “por referência”? Depende...

O compilador Nim determina automaticamente se um parâmetro é passado por valor ou por referência com base no tamanho do tipo de parâmetro.

Se um parâmetro precisar ser passado por valor ou por referência (como ao fazer interface com uma biblioteca C), use os pragmas {.bycopy.} ou {.byref.}.

Nim passa objetos maiores que 3 * sizeOf (int) por referência para desempenho, mas isso é arquitetura e implementação definidas. Portanto, as informações a seguir são apenas uma aproximação para x86_64:

Declaração Valor ou referência? Implícito ou explícito? Gerenciado ou não gerenciado? Observações
symbol: int Por valor Implícito Gerenciado Uso frequente
symbol: var int Por referência Implícito Gerenciado Uso frequente
symbol: ref int Por referência Explícito Gerenciado Raro
symbol: ptr int Por referência Explícito Não gerenciado C/C++ FFI
symbol: var ref int Por referência Implícito Gerenciado Raro
symbol: var ptr int Por referência Implícito Não gerenciado Raro
symbol: pointer Por referência Explícito Ponteiro não gerenciado C/C++ FFI

Intervalos

Em Python, um número inteiro simples para loops usa a função geradora range. Para as formas de argumento 1 e 2 dessa função, o .. iterator de nim funciona quase da mesma maneira:

for i in 0 .. 10:
  echo i  # Prints 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

for i in 5 .. 10:
  echo i  # Prints 5, 6, 7, 8, 9, 10

Observe que o operador .. inclui o final do intervalo, enquanto o range (a, b) do Python não inclui b. Se você preferir esse comportamento, use o iterador .. < em vez disso:

for i in 0 ..< 10:
  echo i  # Prints 0, 1, 2, 3, 4, 5, 6, 7, 8, 9

O range () do Python também tem um terceiro parâmetro opcional, que é o valor a ser incrementado em cada etapa. Isso pode ser positivo ou negativo. Se você precisar desse comportamento, use os iteradores countup ou countdown:

for i in countup(1, 10, 2):
  echo i  # Prints 1, 3, 5, 7, 9

for i in countdown(9, 0, 2):
  echo i  # Prints 9, 7, 5, 3, 1

Converta de range para seq:

import sequtils

const subrange = 0..9
const seqrange = toSeq(subrange)
assert seqrange is seq[int]

Veja também:

Fatias

A sintaxe dos intervalos de fatias é diferente. a [x:y] do Python é a [x.. < y] de Nim.

let variable = [1, 2, 3, 4]
assert variable[0 .. 0] == @[1]
assert variable[0 .. 1] == @[1, 2]
assert variable[0 ..< 2] == @[1, 2]
assert variable[0 .. 3] == @[1, 2, 3, 4]

Fatias de índice reverso

Em Nim, um índice reverso ou índice inverso usa ^ com o número, como ^1. Os índices retroativos têm um tipo específico backwardsIndex e também podem ser “preparados” em tempo de compilação como const:

const lastOne = ^1  # Compile-time
assert lastOne is BackwardsIndex
assert [1, 2, 3, 4, 5][2 .. lastOne] == @[3, 4, 5]
assert [1, 2, 3, 4, 5][2 .. ^1] == @[3, 4, 5]
var another = ^3    # Run-time
assert [1, 2, 3, 4, 5][0 .. another] == @[1, 2, 3]
assert [1, 2, 3, 4, 5][^3 .. ^1] == @[3, 4, 5]  # 2 Reverse index

Verificação estática de limites

Vamos comparar exemplos muito simplificados:

[0, 1, 2][9]  # No Index 9

Isso falha em tempo de execução porque não há índice 9:

$ python3 example.py
Traceback (most recent call last):
  File "example.py", line 1, in <module>
    [0, 1, 2][9]
IndexError: list index out of range

$

Vamos ver Nim:

discard [0, 1, 2][9] # No Index 9

Compile e execute:

$ nim compile --run example.nim
example.nim(1, 19) Warning: can prove: 9 > 2  [IndexCheck]
example.nim(1, 18) Error: index 9 not in 0..2 [0, 1, 2][9]

$

Nim verifica em tempo de compilação que [0, 1, 2] não tem índice 9, porque 9 > 2. Portanto, ele não será compilado nem executado.

Isso também funciona com o Subrange. Digamos que você tenha uma variável inteira que deva ser positiva:

let must_be_positive: Positive = -9

Compile e execute:

$ nim compile --run example.nim
example.nim(1, 34) Warning: can prove: 1 > -9 [IndexCheck]
example.nim(1, 34) Error: conversion from int literal -9 to Positive is invalid.

$

Nim verifica em tempo de compilação se must_be_positive não é Positivo porque 1 > -9. Ele não será compilado nem executado.

Outro exemplo:

var variable0: 5..8 = 5        # int range type, value must be between '5' and '8'.
variable0 = 8
variable0 = 7
assert not compiles(variable0 = 4)
assert not compiles(variable0 = 9)
assert not compiles(variable0 = 0)
assert not compiles(variable0 = -1)
assert not compiles(variable0 = -9)


var variable1: 3.3..7.5 = 3.3  # float range type, value must be between '3.3' and '7.5'.
variable1 = 7.5
variable1 = 5.5
assert not compiles(variable1 = 3.2)
assert not compiles(variable1 = 7.6)
assert not compiles(variable1 = 0.0)
assert not compiles(variable1 = -1.0)
assert not compiles(variable1 = -9.0)


var variable2: 'b'..'f' = 'b'  # char range type, value must be between 'b' and 'f'.
variable2 = 'f'
variable2 = 'c'
assert not compiles(variable2 = 'g')
assert not compiles(variable2 = 'a')
assert not compiles(variable2 = 'z')
assert not compiles(variable2 = '0')
assert not compiles(variable2 = '9')


var variable3: Positive = 1    # Positive type, value must be > 0.
variable3 = 1
variable3 = 999
assert not compiles(variable3 = 0)
assert not compiles(variable2 = -1)
assert not compiles(variable2 = -9)


var variable4: Natural = 0     # Natural type, value must be >= 0.
variable4 = 1
variable4 = 999
assert not compiles(variable4 = -1)
assert not compiles(variable4 = -9)

Você pode controlar isso com —staticBoundChecks:on ou —staticBoundChecks:off.

Com —staticBoundChecks:off, isso pode gerar um erro em tempo de execução, como o Python faz.

Coalescência nula

O Python não tem um operador de coalescência nula (no momento em que este artigo foi escrito).

Em vez disso, os programadores de Python usam o operador condicional ternário:

other = bar if bar is not None else "default value"  # "bar" may be null?, or not ?.

Nim tem um módulo wrapnils com um ?. operador de coalescência nula, que simplifica o código ao reduzir a necessidade de ramificações if.. elif... else em torno de valores intermediários que podem ser nulos.

assert ?.foo.bar.baz == ""  # "bar" may be Null?, or not ?.

Null é None em Python. Null é nil em Nim.

Veja: https://nim-lang.github.io/Nim/wrapnils.html

With Context Manager

Não há equivalente nativo direto à construção with do Python. No Nim, existem as seguintes opções:

Consulte a seção Modelos para ver exemplos.

Strings

Lang String String de várias linhas String Raw String Raw de múltiplas linhas Literais formatados Quote (mascarando)
🐍 Python "foo" """"foo""" r"foo" r"""foo""" f"" "{1 + 2}" "" "``'
👑 Nim "foo" """"foo""" r"foo" r"""foo""" fmt"" "{1 + 2}" "" "

Operações de string

Operações 🐍 Python 👑 Nim
Lower "ABCD” .lower () "ABCD".toLowerAscii()
Strip ” ab “.strip () ” ab “.strip ()
Split "a,b,c".split(",") "a,b,c".split(",")
Concatenação "a” + “b" "a” e “b"
Find "abcd".find("c") "abcd".find("c")
Começa com "abc".startswith("ab") "abc".startswith("ab")
Termina com "abc".endswith("ab") "abc".endswith("ab")
Dividindo por Linha "1\n2\n3".splitlines() "1\n2\n3".splitlines()
Fatiando "abcd"[0:2] "abcd"[0 ..< 2]
Cortando 1 caractere "abcd"[2] "abcd"[2]
Corte reverso "abcd"[-1] "abcd"[^1]
Normalize unicodedata.normalize("NFC", "Foo") "Foo".normalize()
Contando Linhas len("1\n2\n3".splitlines()) "1\n2\n3".countLines()
Repeat (repetir) "foo" * 9 "foo".repeat(9)
Recuar (indent) textwrap.indent("foo", " " * 9) "foo".indent(9)
Unindent [textwrap.dedent(“foo”)](https://docs.python.org/3/library/textwrap.html#textwrap.dedent
“textwrap.dedent () Remove TODA a indentação!”) "foo".unindent(9)
Parse Bool [bool(distutils.util.strtobool (“fALse”)) ](https://stackoverflow.com/q/715417
“bool ('FalSE') == True”) :pergunta: parseBool("fALse")
Parse Int int("42") parseInt("42")
Parse Float float("3.14") parseFloat("3.14")
Literais de string formatados f"foo {1 + 2} bar {variable}" fmt"foo {1 + 2} bar {variable}"
Distância de Levenshtein editDistance (“Kitten”, “Bitten”)

Eficiência de String

Strings podem ser alocadas em memória de uma só vez com newStringOfCap (capacity = 42) , que retorna uma nova string vazia "", mas com capacidade alocada de 42. Se você ultrapassar a capacidade, ela não falhará nem estourará o buffer:

variable = ""
assert variable == "" # length is 0, capacity is 0, 1 allocations, 0 copies
variable += "a"       # length is 1, capacity is 1, 2 allocations, 1 copies
variable += "b"       # length is 2, capacity is 2, 3 allocations, 2 copies
variable += "c"       # length is 3, capacity is 3, 4 allocations, 3 copies
variable += "d"       # length is 4, capacity is 4, 5 allocations, 4 copies
assert variable == "abcd" 
# TOTAL: 5 allocations, 4 copies

⬆️ Python ⬆️          ⬇️ Nim ⬇️

var variable = newStringOfCap(2)
assert variable == "" # length is 0, capacity is 2, 1 allocations, 0 copies
variable.add "a"      # length is 1, capacity is 2, 1 allocations, 0 copies
variable.add "b"      # length is 2, capacity is 2, 1 allocations, 0 copies
variable.add "c"      # length is 3, capacity is 3, 2 allocations, 0 copies
variable.add "d"      # length is 4, capacity is 4, 3 allocations, 0 copies
assert variable == "abcd" 
# TOTAL: 3 allocations, 0 copies

Essa diferença pode ficar maior para strings dentro de laços for ou while.

F-Strings

Nim strformat implementa literais de string formatados inspirados nas f-strings do Python. O strformat é implementado usando metaprogramação e a expansão do código é feita em tempo de compilação. Também funciona para o destino JavaScript.

Semelhante às f-strings do Python, você pode depurar o valor da chave dentro da string usando um sinal de igual. fmt "{key=}" se expande para fmt"key= {value} ":

let x = "hello"
assert fmt"{x=}" == "x=hello"
assert fmt"{x   =  }" == "x   =  hello"

Nim strformat suporta barra invertida, enquanto as strings f do Python não:

>>> print( f"""{ "yep\nope" }""" ) # Run-time error.
Error: f-string expression part cannot include a backslash.

⬆️ Python ⬆️          ⬇️ Nim ⬇️

echo fmt"""{ "yep\nope" }"""       # Nim works.

yep
ope

Você pode escolher um par de caracteres personalizado para abrir e fechar a formatação dentro da string passando o char como argumento:

import std/strformat
let variable = 42
assert fmt("( variable ) { variable }", '(', ')') == "42 { variable }"
assert fmt("< variable > { variable }", '<', '>') == "42 { variable }"

Usar caracteres como apóstrofo invertido e espaço ' 'funciona:

import std/strformat
let variable = 42
assert fmt(" variable`{variable}", ' ', '`') == "42{variable}"

String Raw

A string raw do Python não pode terminar em "\", mas a string raw do Nim funciona muito bem:

>>> print(r"\")  # Run-time error.
SyntaxError: EOL while scanning string literal.

⬆️ Python ⬆️          ⬇️ Nim ⬇️

nim> echo r"\"
"\"

Isso pode ser relevante ao trabalhar com cadeias de caracteres que usam "\", como caminhos de sistemas de arquivos r” C:\mypath\ ", etc.

Equivalentes de biblioteca padrão

Use 🐍 Python 👑 Nim
Sistema operacional os os
Operações de string string strutils
Data e hora datetime times
Random random random
Expressões regulares (Backend) re re
Expressões regulares (Frontend) jsre
HTTP urllib httpclient
Logando logging registro
Executando comandos externos subprocess osproc
Manipulação de caminhos (path) pathlib, os.path os
Matemática math, cmath math
Tipos MIME tipos MIME mimetypes
SQLite SQL sqlite3 db_sqlite
Postgres SQL db_postgres
Distância de Levenshtein editdistance
Serialização pickle json, jsonutils,
marshal
Base64 base64 base64
Abre o URL do navegador da web webbrowser browsers
Assíncrono asyncio asyncdispatch, asyncfile,
asyncnet, asyncstreams
Testes unitários unittest unittest
Diff difflib diff
Cores colorsys colors
MD5 hashlib.md5 md5
SHA1 hashlib.sha1 sha1
Servidor HTTP http.server asynchttpserver
Lexer shlex lexbase
Multi-Threading threading threadpool
URL E URI urllib.parse uri
CSV csv parsecsv
Parâmetros da linha de comando argparse parseopt
SMTP smtplib smtp
Cookies HTTP http.cookies cookies
Estatísticas statistics stats
Empacotamento de texto (Text Wrapping) textwrap wordwrap
Registro do Windows winreg registry
POSIX posix posix, posix_utils
SSL ssl openssl
CGI cgi cgi
Profiler cprofile, profile nimprof
Tempo monotônico time.monotonic monotimes
Executar funções na saída atexit exitprocs
Definir permissões de arquivo os, stat os, filepermissions
Percurso recursivo do sistema de arquivos os.walk os.walkDirRec,
globs.walkDirRecFilter
Mecanismo de modelagem string.Template Source Code Filters
Deques collections.deque deques
Dicionário ordenado baseado na árvore B btreetables
Critical Bit Tree Dict/Set critbits
Alocação de memória agrupada pools
Analisar JSON json parsejson, json
Analisar INI configparser parsecfg
Analisar XML xml parsexml, xmltree
Analisar HTML html.parser htmlparser
Analisar SQL parsesql
Cores no terminal terminal
Detecção de distribuição Linux distros
Gerador de HTML htmlgen
Funções de seta (arrow) sugar
In-Place para Work-on-Copy sugar.dup
Sintaxe Sugar sugar
JavaScript e frontend dom, asyncjs,
jscore, jsffi,
dom_extensions, jsre

Tuplas

As tuplas têm tamanho fixo, começam no índice 0, podem conter tipos mistos e podem ser anônimas ou nomeadas. Tuplas nomeadas não têm nenhuma sobrecarga extra sobre tuplas anônimas.

Tupla anônima

(1, 2, 3)

⬆️ Python ⬆️          ⬇️ Nim ⬇️

(1, 2, 3)

Tuple Nomeada

  • O Nim permite que os campos sejam nomeados, sem exigir que a própria tupla seja nomeada. O Python NamedTuple requer import collections, e precisamos dar a ele um nome fictício de sublinhado:
collections.namedtuple("_", "key0 key1")("foo", 42)

⬆️ Python ⬆️          ⬇️ Nim ⬇️

(key0: "foo", key1: 42)

Tuple Nomeada

  • Também podemos nomear os campos e a tupla:
collections.namedtuple("NameHere", "key0 key1")("foo", 42)

⬆️ Python ⬆️          ⬇️ Nim ⬇️

type NameHere = tuple[key0: string, key1: int]
var variable: NameHere = (key0: "foo", key1: 42)

tuple [key0: string, key1: int] é o typedesc para Declarações. (key0: “foo”, key1:42) é o literal para atribuições.

As tuplas do Nim são muito parecidas com Python NamedTuple, pois os membros da tupla têm nomes.

NÃO use tuplas nomeadas para “imitar” objetos apropriados (o compilador reutiliza instanciações genéricas para tuplas “idênticas”).

Veja manual para uma análise mais aprofundada das tuplas.

Listas

Nim sequences não são de tamanho fixo. Eles podem crescer e encolher, começar no índice 0 e devem conter elementos do mesmo tipo.

["foo", "bar", "baz"]

⬆️ Python ⬆️          ⬇️ Nim ⬇️

@["foo", "bar", "baz"]

@ é uma função que converte de array para seq.

List Comprehensions

variable = [item for item in (-9, 1, 42, 0, -1, 9)]

⬆️ Python ⬆️          ⬇️ Nim ⬇️

let variable = collect(newSeq):
  for item in @[-9, 1, 42, 0, -1, 9]: item

Uma comprehension também pode ser atribuída a const e será executada em tempo de compilação.

A comprehension é implementada como uma macro que é expandida em tempo de compilação. Você pode ver o código expandido usando a opção de compilador —expandMacro:

let variable = 
  var collectResult = newSeq(Natural(0))
  for item in items(@[-9, 1, 42, 0, -1, 9]):
    add(collectResult, item)
  collectResult

As comprehension podem ser aninhadas, com várias linhas e com várias expressões, tudo sem sobrecarga:

import std/sugar

let values = collect(newSeq):
  for val in [1, 2]:
    collect(newSeq):
      for val2 in [3, 4]:
        if (val, val2) != (1, 2):
          (val, val2)
        
assert values == @[@[(1, 3), (1, 4)], @[(2, 3), (2, 4)]]

Exemplo de linha única:

print([i for i in range(0, 9)])

⬆️ Python ⬆️          ⬇️ Nim ⬇️

echo(block: collect newSeq: (for i in 0..9: i))

As comprehension de Python convertem o código em um gerador, mas as compreensões de Nim não convertem o código em um iterador:

import std/sugar

func example() =
  discard collect(newSeq):
    for item in @[-9, 1, 42, 0, -1, 9]: 
      if item == 0: return
      item

example()

⬆️ Nim ⬆️          ⬇️ Python ⬇️

def example():
  [item for item in [-9, 1, 42, 0, -1, 9] if item == 0: return]
      
example()

Python reclama:

SyntaxError: invalid syntax.

Algumas coisas que em Python são sintaticamente proibidas dentro de compreensões (como return) são permitidas no Nim. Isso ocorre porque as compreensões do Nim são apenas macros que se expandem para o código normal.

  • O que é collect () ?.

collect takes as argument whatever your returning type uses as the constructor.

Dict Comprehensions

variable = {key: value for key, value in enumerate((-9, 1, 42, 0, -1, 9))}

⬆️ Python ⬆️          ⬇️ Nim ⬇️

let variable = collect(initTable(4)):
  for key, value in @[-9, 1, 42, 0, -1, 9]: {key: value}

Set Comprehensions

variable = {item for item in (-9, 1, 42, 0, -1, 9)}

⬆️ Python ⬆️          ⬇️ Nim ⬇️

let variable = collect(initHashSet):
  for item in @[-9, 1, 42, 0, -1, 9]: {item}

Conjuntos (sets)

Lang Conjunto (set) Set ordenado Bitset Bit Fields Importações
🐍 Python set()
👑 Nim HashSet() OrderedSet() set Bit Fields import std/sets
  • O Python set pode ser substituído por HashSet .

Os conjuntos Python não são como o tipo de conjunto Nim. O conjunto “padrão” é um bitset. Para cada valor possível do tipo contido, ele armazena 1 bit indicando se ele está presente no conjunto. Isso significa que você deve usá-lo se o tipo tiver uma faixa finita e limitada de valores possíveis. Se todos os valores possíveis também forem conhecidos em tempo de compilação, você poderá criar um Enum para eles.

O maior número inteiro que você pode caber em um conjunto normalmente é 65535 igual a high (uint16) .

Você pode ajustar números inteiros maiores usando um Subrange inteiro, se não precisar de números inteiros pequenos. Um exemplo realmente estressante de definir para caber 2_147_483_647 é igual a high (int32) em um conjunto em tempo de compilação:

const x = {range[2147483640..2147483647](2147483647)}
assert x is set  # Equals to {2147483647}

O tipo de conjunto (set) básico do Nim é rápido e economiza memória, em comparação com o HashSet, que é implementado como um dicionário. Para tipos de flags simples e pequenos conjuntos matemáticos, use set. Para coleções maiores, ou se você está apenas aprendendo, use o HashSet.

Dicionários

Use tables para dicionários semelhantes ao Python.

Lang Dicionário Dicionário ordenado Counter Importações
🐍 Python dict() OrderedDict() Counter() import std/collections
👑 Nim Table() OrderedTable() CountTable() import std/tables

Construtores de tabela

dict(key="value", other="things")

⬆️ Python ⬆️          ⬇️ Nim ⬇️

to_table({"key": "value", "other": "things"})

Dicionário ordenado

collections.OrderedDict([(8, "hp"), (4, "laser"), (9, "engine")])

⬆️ Python ⬆️          ⬇️ Nim ⬇️

to_ordered_table({8: "hp", 4: "laser", 9: "engine"})

Contadores

collections.Counter(["a", "b", "c", "a", "b", "b"])

⬆️ Python ⬆️          ⬇️ Nim ⬇️

to_count_table("abcabb")

**Exemplos: **

import std/tables

var dictionary = to_table({"hi": 1, "there": 2})

assert dictionary["hi"] == 1
dictionary["hi"] = 42
assert dictionary["hi"] == 42

assert len(dictionary) == 2
assert dictionary.has_key("hi")

for key, value in dictionary:
  echo key, value

As tabelas são apenas açúcar de sintaxe para uma matriz de tuplas:

assert {"key": "value", "k": "v"} == [("key", "value"), ("k", "v")]
assert {"key": true, "k": false} == @[("key", true),  ("k", false)]

Tabelas Árvores-B

Tabelas ordenadas genéricas baseadas em Árvore-B usando a mesma API.

Veja também:

Operadores ternários

"result0" if conditional else "result1"

⬆️ Python ⬆️          ⬇️ Nim ⬇️

if conditional: "result0" else: "result1"

Em Nim, o “operador ternário” é simplesmente um if.. else em linha. Ao contrário do Python, o if.. else comum é uma expressão, portanto, ele pode ser atribuído a uma variável. Esses trechos são equivalentes:

var foo = if 3 < 10:
  50
  else: 100
var foo = if 3 < 10: 50 else: 100

Lendo e escrevendo arquivos

Lendo arquivos linha por linha

with open("yourfile.txt", "r") as f:
    for line in f:
        print(line)

⬆️ Python ⬆️          ⬇️ Nim ⬇️

for line in lines("yourfile.txt"):
  echo line

**Lendo e gravando arquivos: **

write_file("yourfile.txt", "this string simulates data")
assert read_file("yourfile.txt") == "this string simulates data"

**Lendo arquivos em tempo de compilação: **

const constant = static_read("yourfile.txt")  # Returns a string at compile-time

Alterar permissões de arquivo

import std/os
os.chmod("file.txt", 0o777)

⬆️ Python ⬆️          ⬇️ Nim ⬇️

import fusion/filepermissions
chmod "file.txt", 0o777

Esses exemplos assumem que existe um arquivo ” file.txt “. Ambos usam as permissões octais de arquivo Unix. Além disso, uma API de nível inferior está disponível no módulo os.

Veja https://nim-lang.github.io/fusion/src/fusion/filepermissions.html

Alterar pasta temporariamente

import std/os

class withDir:
    # Unsafe without a __del__()

    def __init__(self, newPath):
        self.newPath = os.path.expanduser(newPath)

    def __enter__(self):
        self.savedPath = os.getcwd()
        os.chdir(self.newPath)

    def __exit__(self, etype, value, traceback):
        os.chdir(self.savedPath)


with withDir("subfolder"):
  print("Inside subfolder")
print("Go back outside subfolder")

⬆️ Python ⬆️          ⬇️ Nim ⬇️

import fusion/scripting

withDir "subfolder":
  echo "Inside subfolder"
echo "Go back outside subfolder"

Esses exemplos assumem que existe uma pasta "subfolder". Opcionalmente, o Python tem dependências de terceiros que fazem a mesma coisa; os exemplos usam a biblioteca padrão. Algumas dependências de terceiros do Python podem converter o código dentro de withDir em um gerador, forçando você a alterar o código (como return para yield etc), exemplos usam biblioteca padrão.

Veja https://nim-lang.github.io/fusion/src/fusion/scripting.html

Map e filter

def isPositive(arg: int) -> bool: 
  return arg > 0

map(isPositive, [1, 2,-3, 5, -9])
filter(isPositive, [1, 2,-3, 5, -9])

⬆️ Python ⬆️          ⬇️ Nim ⬇️

proc isPositive(arg: int): bool = 
  return arg > 0 

echo map([1, 2,-3, 5, -9], isPositive)
echo filter([1, 2,-3, 5, -9], isPositive)

Lambdas

variable: typing.Callable[[int, int], int] = lambda var1, var2: var1 + var2

⬆️ Python ⬆️          ⬇️ Nim ⬇️

var variable = proc (var1, var2: int): int = var1 + var2

Exemplo em várias linhas:

var anon = func (x: int): bool =
             if x > 0:
               result = true
             else: 
               result = false

assert anon(9)

As funções anônimas do Python não podem usar return, mas podem no Nim:

example = lambda: return 42
assert example() == 42

Reclamações SyntaxError: sintaxe inválida.

⬆️ Python ⬆️          ⬇️ Nim ⬇️

let example = func: int = return 42
assert example() == 42

As funções anônimas do Python não podem usar yield, mas podem no Nim:

example = lambda: for i in range(0, 9): yield i

for _ in example(): pass

Reclamações SyntaxError: sintaxe inválida.

⬆️ Python ⬆️          ⬇️ Nim ⬇️

let example = iterator: int = 
  for i in 0..9: yield i

for _ in example(): discard

Processos anônimos em Nim são basicamente funções sem nome.

Decoradores

  • Modelos e macros podem ser usados de forma semelhante aos decoradores do Python.
def decorator(argument):
  print("This is a Decorator") 
  return argument

@decorator
def function_with_decorator() -> int:
  return 42

print(function_with_decorator())

⬆️ Python ⬆️          ⬇️ Nim ⬇️

template decorator(argument: untyped) =
  echo "This mimics a Decorator"
  argument

func function_with_decorator(): int {.decorator.} =
  return 42

echo function_with_decorator()
  • Por que o Nim não usa a sintaxe @decorator?.

Nim usa {. e.} porque pode ter vários decoradores juntos.

Também no Nim, trabalha-se com variáveis e tipos:

func function_with_decorator(): int {.discardable, inline, compiletime.} =
  return 42

let variable {.compiletime.} = 1000 / 2

type Colors {.pure.} = enum Red, Green, Blue

@ é uma função que converte de array para seq.

JSON

Python usa strings de várias linhas com JSON interno, Nim usa JSON literal diretamente no código.

import std/json

variable = """{
    "key": "value",
    "other": true
}"""
variable = json.loads(variable)
print(variable)

⬆️ Python ⬆️          ⬇️ Nim ⬇️

import json

var variable = %*{
  "key": "value",
  "other": true
}
echo variable
  • %* converte tudo dentro dos colchetes em JSON, o JSON tem um tipo JsonNode.
  • %* pode ter variáveis e literais dentro dos colchetes.
  • O JSON pode ter comentários de Nim entre colchetes de %*.
  • Se o JSON não for um JSON válido, o código não será compilado.
  • JsonNode pode ser útil no Nim porque é um tipo que pode ter tipos mistos e pode crescer/encolher.
  • Você pode ler o JSON em tempo de compilação e armazená-lo em uma constante como uma string.
  • Para analisar o JSON a partir de uma string, você pode usar parseJSON(“{}”) .
  • Para analisar o JSON de um arquivo, use parseFile(“file.json”) .
  • Documentação JSON

Auto-execução do módulo principal

if __name__ == "__main__":
  main()

⬆️ Python ⬆️          ⬇️ Nim ⬇️

when is_main_module:
  main()

Testes unitários

import std/unittest


def setUpModule():
    """Setup: Run once before all tests in this module."""
    pass

def tearDownModule():
    """Teardown: Run once after all tests in this module."""
    pass


class TestName(unittest.TestCase):
    """Test case description"""

    def setUp(self):
        """Setup: Run once before each tests."""
        pass

    def tearDown(self):
        """Teardown: Run once after each test."""
        pass

    def test_example(self):
        self.assertEqual(42, 42)


if __name__ == "__main__":
    unittest.main()

⬆️ Python ⬆️          ⬇️ Nim ⬇️

import std/unittest

suite "Test Name":

  echo "Setup: Run once before all tests in this suite."

  setup:
    echo "Setup: Run once before each test."

  teardown:
    echo "Teardown: Run once after each test."

  test "example":
    assert 42 == 42

  echo "Teardown: Run once after all tests in this suite."

Assert com mensagens personalizadas

  • assert pode ter um block. Você pode personalizar a mensagem para melhorar a experiência do usuário:
let a = 42
let b = 666
doAssert a == b, block:
  ("\nCustom Error Message!:" &
   "\n  a equals to " & $a &
   "\n  b equals to " & $b)

Testamento

Uma alternativa ao unittest. Está preparado para grandes projetos e tem mais funcionalidades.

Docstrings

Docstrings em Nim são comentários reStructuredText e MarkDown começando com ##. ReStructuredText e MarkDown podem ser misturados, se você quiser.

Gere documentação HTML, Latex (PDF) e JSON a partir do código-fonte com nim doc file.nim.

O Nim pode gerar um gráfico de dependência DOT .dot com nim genDepend file.nim.

Você pode executar a documentação como Unittests com runnableExamples.

"""Documentation of module"""

class Kitten(object):
    """Documentation of class"""

    def purr(self):
        """Documentation of method"""
        print("Purr Purr")

⬆️ Python ⬆️          ⬇️ Nim ⬇️

## Documentation of Module *ReSTructuredText* and **MarkDown**

type Kitten = object ## Documentation of type
  age: int  ## Documentation of field

proc purr(self: Kitten) =
  ## Documentation of function
  echo "Purr Purr"

Indentação opcional

Você pode escrever construções como if.. then e try.. except.. finally em uma única linha sem erros ou avisos; a indentação é opcional. Obviamente, isso só é uma boa ideia se o trecho for curto e simples.

let a = try: 1 + 2 except: 42 finally: echo "Inline try"

let b = if true: 2 / 4 elif false: 4 * 2 else: 0

for i in 0 .. 9: echo i

proc foo() = echo "Function"

(proc   () = echo "Anonymous function")()

template bar() = echo "Template"

macro baz() = echo "Macro"

var i = 0
while i < 9: i += 1

when is_main_module: echo 42

CamelCase

  • Por que Nim CamelCase em vez de snake_case?

Realmente não é, Nim é independente de estilo!

let camelCase = 42      # Declaring as camelCase 
assert camel_case == 42 # Using as snake_case

let snake_case = 1      # Declaring as snake_case
assert snakeCase == 1   # Using as camelCase

let `free style` = 9000
assert free_style == 9000

**Esse recurso permite que o Nim interopere perfeitamente com várias linguagens de programação com diferentes estilos de nomeação. **

Para obter um código mais homogêneo, você pode aplicar um estilo de caixa padrão usando o comando do compilador —styleCheck:hint. Nim verificará o estilo do seu código antes da compilação, semelhante ao pycodestyle em Python. Se você quiser um estilo ainda mais rígido, você pode usar —styleCheck:error.

O Nim vem com um formatador automático de código embutido chamado Nimpretty.

Muitas linguagens de programação não diferenciam maiúsculas e minúsculas, como: PowerShell, SQL, PHP, Lisp, Assembly, Batch, ABAP, Ada, Visual Basic, VB.NET, Fortran, Pascal, Forth, Cobol, Scheme, Red, Rebol.

Se você está começando do zero, pode usar nomes semelhantes aos do Python enquanto aprende. Isso não produzirá um erro, a menos que você diga ao compilador que deseja isso.

def x proc/func

  • Por que Nim não usa def em vez de proc?.

Nim usa proc para funções normais, que é a abreviação de “procedimento”.

Use func para quando sua rotina não puder e não deve acessar variáveis globais ou locais de thread (veja também: funções puras).

Nim tem rastreamento de efeitos colaterais.

Você não pode usar echo dentro de func, porque echo muda stdout, o que é um efeito colateral. Em vez disso, use debugEcho.

Veja também:

Se você está começando do zero, você pode usar proc para todas as funções enquanto aprende. Isso não dará um erro.

Assíncrono

O Nim tem a assíncrona embutida há muito tempo. Funciona como você pode esperar com async, await, Future, etc.

asyncdispatch é um módulo para escrever código simultâneo usando a sintaxe async/await.

Future é um tipo (como uma Future em Python ou uma Promise em JavaScript).

{.async.} é um pragma que converte funções em assíncronas (como async def em Python).

Vamos converter o asyncio oficial do Python Hello World para Nim:

async def main():
    print("Hello ...")
    await asyncio.sleep(1)
    print("... World!")

asyncio.run(main())

⬆️ Python ⬆️          ⬇️ Nim ⬇️

proc main() {.async.} =
  echo("Hello ...")
  await sleep_async(1)
  echo("... World!")

wait_for main()

A assíncrona interna é implementada usando metaprogramação (macros, modelos, pragmas, etc.).

Descrição AsyncCheck waitFor await
Espera que a Future seja concluída ✔️ ✔️
Ignora a Future ✔️
Retorna o resultado da Future ✔️ ✔️
Disponível apenas dentro do async ✔️
  • Por que Nim não usa async def?.

Async é apenas uma macro em Nim, sem necessidade de alterar a sintaxe da linguagem. É como um decorador em Python, só que mais poderoso.

Também no Nim, a mesma função pode ser assíncrona e síncrona ao mesmo tempo, com o mesmo código, com o mesmo nome.

Em Python, quando você tem uma biblioteca "foo", você pode precisar de foo (sync) e aiofoo (async), que geralmente estão em projetos, repositórios, desenvolvedores e APIs completamente diferentes. Isso não é necessário no Nim, ou raramente visto, graças a esse recurso.

Como o async é apenas uma macro no Nim, você também pode criar seu próprio assíncrono do seu jeito.

Veja também asyncfile, asyncnet, asyncstreams, asyncftpclient, asyncfutures.

Eu tenho que saber C?

Você nunca precisa realmente editar manualmente o C, da mesma forma que no Python você nunca edita manualmente os arquivos .pyc.

No Nim, você codifica escrevendo Nim, da mesma forma que em Python você codifica escrevendo Python.

Modelos (Templates)

Um modelo substitui sua invocação pelo corpo do modelo em tempo de compilação.

Essencialmente, o compilador copiará e colará um pedaço de código para você.

Um modelo nos permite ter construções semelhantes a uma função sem qualquer sobrecarga ou dividir funções enormes em partes menores.

Muitos nomes de funções e variáveis podem poluir o namespace local. As variáveis dentro dos modelos não existem fora do modelo. Os modelos não existem no namespace em tempo de execução (se você não os exportar). Os modelos podem otimizar determinados valores se forem conhecidos em tempo de compilação.

Os templates não podem importar nem exportar de bibliotecas automaticamente implicitamente. Os modelos não “importam automaticamente” símbolos usados dentro de si mesmos. Se você usar qualquer biblioteca importada no corpo de um modelo, deverá importar essa biblioteca ao invocar esse modelo.

Dentro dos modelos, você não pode usar return porque um modelo não é uma função.

Os modelos permitem que você implemente APIs bonitas e de alto nível para o uso diário, mantendo os detalhes otimizados de baixo nível fora de sua cabeça e DRY.

Python with open (” file.txt “, mode = “r”) as file: implementado usando um modelo:

![Animação explicativa do modelo](https://raw.githubusercontent.com/juancarlospaco/nim-presentation-slides/master/templates.gif “Animação explicativa do modelo”)

O GIF não é perfeito, mas uma aproximação preguiçosa e simplificada!

Essa não é a maneira de ler arquivos no Nim, apenas um exercício.

Este modelo não é perfeito, mas uma aproximação preguiçosa. É um exercício para o leitor tentar melhorá-lo ;P

template withOpen(name: string, mode: char, body: untyped) =
  let flag = if mode == 'w': fmWrite else: fmRead  # "flag" doen't exist outside of this template
  let file {.inject.} = open(name, flag)   # Create and inject `file` variable, `file` exists outside of this template because of {.inject.}
  try:
    body                                   # `body` is the code passed as argument
  finally:
    file.close()                           # Code after the code passed as argument

withOpen("testing.nim", 'r'): # Mimic Python with `open("file", mode='r') as file`
  echo "Hello Templates"      # Code inside the template, this 2 lines are "body" argument on the template
  echo file.read_all()        # This line uses "file" variable

Se você está começando do zero, não se preocupe, você pode usar funções para tudo enquanto aprende.

Como compartilhar variáveis entre funções?

O compartilhamento de variáveis entre funções é semelhante ao Python.

**Variável global: **

global_variable = ""

def function0():
    global global_variable
    global_variable = "cat"

def function1():
    global global_variable
    global_variable = "dog"

function0()
assert global_variable == "cat"
function1()
assert global_variable == "dog"
function0()
assert global_variable == "cat"

⬆️ Python ⬆️          ⬇️ Nim ⬇️

var global_variable = ""

proc function0() =
  global_variable = "cat"

proc function1() =
  global_variable = "dog"

function0()
assert global_variable == "cat"
function1()
assert global_variable == "dog"
function0()
assert global_variable == "cat"

**Atributo do objeto: **

class IceCream:

  def __init__(self):
    self.object_attribute = None

def function_a(food):
    food.object_attribute = 9

def function_b(food):
    food.object_attribute = 5

food = IceCream()
function_a(food)
assert food.object_attribute == 9
function_b(food)
assert food.object_attribute == 5
function_a(food)
assert food.object_attribute == 9

⬆️ Python ⬆️          ⬇️ Nim ⬇️

type IceCream = object
  object_attribute: int

proc functiona(food: var IceCream) =
  food.object_attribute = 9

proc functionb(food: var IceCream) =
  food.object_attribute = 5

var food = IceCream()
functiona(food)
assert food.object_attribute == 9
functionb(food)
assert food.object_attribute == 5
functiona(food)
assert food.object_attribute == 9

Você pode passar funções como argumentos para outras funções, como em Python. Funções (procs) são objetos de primeira classe.

In-Place x Out-Place

Se você estiver migrando de uma linguagem interpretada, como Python ou JavaScript, você pode encontrar menções estranhas de “In-Place” e “Out-Place” em algum lugar de Nim. Se você não sabe o que isso significa, parece que o Nim tem funções duplicadas.

O Python aloca uma nova string ou objeto quando algo nele muda de alguma forma. Digamos que você tenha uma sequência enorme em uma variável e queira alterar um único caractere. Como as cadeias de caracteres do Python são imutáveis, a única solução é duplicar a string na memória, mas com a nova cópia tendo esse caractere alterado. Devolver uma nova cópia é uma operação “Out-Place”. A maior parte do Python funciona assim.

Por outro lado, as strings de Nim são mutáveis. No Nim, você pode alterar apenas o caractere que deseja alterar, em vez de copiar a string na memória. Algumas funções funcionam no local, outras funcionam em uma nova cópia. A documentação (geralmente) será qual.

usando macro, o Nim pode passar de uma função in-place para uma out-place.

Os módulos Nim stdlib projetados para o destino JavaScript geralmente funcionam em uma nova cópia. É assim que o destino do JavaScript é; não há API local nem benefícios em usá-la.

Alguns módulos Nim stdlib que funcionam em uma nova cópia podem ou não ser alterados para funcionar no local no futuro.

Exemplos:

import std/sugar  # sugar.dup

func inplace_function(s: var string) =  # Does not use "string" but "var string"
  s = "CHANGED"

# In-Place algo.
var bar = "in-place"
inplace_function(bar)  ## Variable mutated in-place.
assert bar == "CHANGED"

# Out-Place algo.
assert "out-place".dup(inplace_function) == "CHANGED"  ## Variable mutated on a new copy.

Importar arquivos Nim em Python

Sintaxe Python para Nim

Publicar no PYPI

Compilação silenciosa

Se você quiser que a compilação fique completamente silenciosa (você pode perder avisos e dicas importantes), você pode adicionar ao comando de compilação —hints:off —verbosity:0.

Ajuda do compilador

A ajuda do compilador é longa. Para torná-lo mais fácil de usar, apenas os comandos mais frequentes são mostrados com —help. Se você quiser ver a ajuda completa, você pode usar —fullhelp.

Modos de construção

Quando seu código estiver pronto para produção, você deve usar uma compilação Release, você pode adicionar ao comando de compilação -d:release.

Característica Versão de compilação Compilação de depuração
Rapidez Rápido Lento
Tamanho do arquivo Pequeno Grande
Otimizado ✔️
Tracebacks (Pilhas de Rastreamento) ✔️
Verificações em tempo de execução ✔️ ✔️
Verificações em tempo de compilação ✔️ ✔️
assert ✔️
doAssert ✔️ ✔️

MicroPython

O Nim compila em C, para que possa ser executado no Arduino e em hardware similar.

Tem várias estratégias de gerenciamento de memória para atender às suas necessidades, incluindo gerenciamento manual completo da memória. Os binários Nim são pequenos quando construídos para o Release e podem caber no pequeno armazenamento do hardware.

SuperCollider

O SuperCollider é C++, então ele pode ser reutilizado usando o Nim.

Teoricamente, os plug-ins do Nim SuperCollider devem ser tão rápidos quanto o código C. A metaprogramação de Nim nos permite criar DSLs compatíveis com LiveCoding.

Alguns projetos para o Nim LiveCoding:

ABC

Veja isso

Filosofia

A chave para entender o Nim é que o Nim foi projetado para ser tão rápido quanto C, mas para ser muito mais seguro. Muitas das decisões de design são baseadas em tornar mais difícil dar um tiro no próprio pé. Em Python, não há ponteiros (tudo é tratado como referência). Enquanto o Nim fornece dicas, o Nim oferece outras ferramentas mais seguras para suas necessidades diárias, enquanto os ponteiros são principalmente reservados para a interface com C e a programação de sistemas de baixo nível.

Ao contrário do Python, a maior parte do código Nim pode ser executada em tempo de compilação para realizar metaprogramação. Você pode fazer muitas das DSLs possíveis com decoradores/metaprogramação Python com macros e pragmas Nim. (E algumas coisas que você não pode!). Obviamente, isso requer alguns padrões diferentes e mais segurança de tipos.

⬆️ ⬆️ ⬆️ ⬆️

Clone this wiki locally