Nim for Python Programmers PT_BR (original) (raw)
Í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:
⬆️ 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.
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:
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"
- https://nim-lang.github.io/Nim/manual.html#statements-and-expressions-block-statement
- https://nim-lang.github.io/Nim/manual.html#statements-and-expressions-block-expression
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 chamarmath.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
⬆️ 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.nimnim c --import:folder/mymodule file.nimnim 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 usarprelude:
include prelude
echo now()
echo getCurrentDir()
echo "Hello $1".format("World")
prelude é um arquivo includeque 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 demodule.foo (), sem UFCS.
Normalmente, o Editor/IDEdeve sugerir de onde vêm os símbolos, como em qualquer outra linguagem de programação:
O Nim vem integrado com o NimSuggestpara 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:
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 visualpara 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 umImportError 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 openarraypara 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 comofunction (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 fazerdef __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 paraFoo, basta criar newFoo () ou initFoo (). Como você pode notar, initCaté apenas uma função que retorna um Cat.
initFoo ()paraobject.newFoo ()pararef 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 |
- Se um símbolo “por valor” for grande, ele será transmitido “por referência” automaticamente.
- Você pode desativar essa otimização usando o pragma {.bycopy.} no símbolo.
- O {.byref.} força a passagem “por referência”, o inverso de {.bycopy.}.
- Nim
seqé passado “por referência” por padrão. - Sua
stringé Copy-On-WriteCOW (ARC/ORC). - A aritmética do ponteiro pode ser executada com o ponteiro.
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 .. iteratorde 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 orange (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 countupou 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 [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, porque9 > 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 é Positivoporque 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.
- Para obter uma documentação melhor, consulte:https://nim-lang.github.io/Nim/drnim.html
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”) |
- **As operações de string Nim requerem import std/strutils.**
- Uma comparação muito detalhada.
Eficiência de String
Strings podem ser alocadas em memória de uma só vez comnewStringOfCap (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}", '<', '>') == "42 {variable}"
Usar caracteres como apóstrofo invertido e espaço ' 'funciona:
assert fmt(" variable{variable}", ' ', '') == "42{variable}"
Usar o mesmo caractere para início e fim funciona:
assert fmt("%variable% {variable}", '%', '%') == "42 {variable}"
fmt"xxx"é quase igual afr"xxx", enquantofmt "xxx"oufmt("xxx")ou&"xxx"é igual af"xxx". (por quê?)- É recomendável ler a documentação completa do strformat.
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 ⬇️
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 | strutils.`%` ou 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 |
- Isso não está completo, mas apenas uma visão geral rápida. Para obter mais informações, consulte https://nim-lang.org/docs/lib.html
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
⬆️ Python ⬆️ ⬇️ Nim ⬇️
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 ⬇️
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 manualpara 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.
⬆️ Python ⬆️ ⬇️ Nim ⬇️
@ é 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: 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: for val in [1, 2]: collect: 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 collect(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: 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 ()?.
collecttakes 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: for key, value in @[-9, 1, 42, 0, -1, 9]: {key: value}
collect ()requer import std/sugar.collect ()com a sintaxe{k: v}(para Tabela Hash) também requer import std/tables.
Set Comprehensions
variable = {item for item in (-9, 1, 42, 0, -1, 9)}
⬆️ Python ⬆️ ⬇️ Nim ⬇️
let variable = collect: for item in @[-9, 1, 42, 0, -1, 9]: {item}
collect ()requer import std/sugar.collect()com a sintaxe{v}(para HashSet) também requer import std/sets.
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 é 65535igual 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 = {range2147483640..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 ⬇️
{"key": "value", "other": "things"}.toTable
Dicionário ordenado
collections.OrderedDict([(8, "hp"), (4, "laser"), (9, "engine")])
⬆️ Python ⬆️ ⬇️ Nim ⬇️
{8: "hp", 4: "laser", 9: "engine"}.toOrderedTable
Contadores
collections.Counter("abcabb")
⬆️ Python ⬆️ ⬇️ Nim ⬇️
**Exemplos: **
import std/tables
var dictionary = {"hi": 1, "there": 2}.toTable
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-Busando 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
lines()Documentação https://nim-lang.org/docs/io.html#lines.i%2Cstring
**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 (comoreturn 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)
- operações de map e filter requerem import std/sequtils.
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 Nimsã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 tipoJsonNode.%*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.
JsonNodepode 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."
- Documentação do Unittest.
- O gerenciador de pacotes Nimble também pode executar Unittests.
- O NIMScript também pode executar Unittests.
- Você pode executar a documentação como Unittests com runnableExamples.
Assert com mensagens personalizadas
assertpode ter umblock. 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.
- https://nim-lang.github.io/Nim/testament.html ** (Recomendado) **
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 comnim doc file.nim.
O Nim pode gerar um gráfico de dependência DOT .dot comnim 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
defem vez deproc?.
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 eDRY.
Python with open (” file.txt “, mode = “r”) as file: implementado usando um 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
- https://github.com/yglukhov/nimpy/wiki#publish-to-pypi
- https://github.com/sstadick/ponim/blob/master/README.md#nim--python--poetry —
- https://github.com/sstadick/nython#nython
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.
- https://github.com/zevv/nim-arduino
- https://github.com/elcritch/nesper#example-code
- https://gitlab.com/endes123321/nimcdl/tree/master#nimcdl-nim-circuit-design-language
- https://github.com/cfvescovo/Arduino-Nim#arduino-nim
- https://gitlab.com/nimbed/nimbed#nimbed
- https://gitlab.com/endes123321/led-controller-frontend#led-controller-frontend
- https://gitlab.com/jalexander8717/msp430f5510-nim
- https://github.com/mwbrown/nim_stm32f3
- https://github.com/gokr/ardunimo
- https://gitlab.com/NetaLabTek/Arduimesp
- https://ftp.heanet.ie/mirrors/fosdem-video/2020/AW1.125/nimoneverything.webm
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:
- https://github.com/vitreo12/omni#omni
- https://github.com/capocasa/scnim#scnim---writing-supercollider-ugens-using-nim
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.