Dicas para portar código de Processing modo Java para modo Python
…e possivelmente vice-versa :)
Considerações gerais
- Como você provavelmente sabe, em Python o que conta para saber se uma linha de código está ‘dentro’ de uma função ou de outra estrutura qualquer, como um
if
, é a indentação. No Java são as chaves{}
que mandam, mas é comum a indentação refletir a hierarquia, mesmo isso não sendo obrigatório. Por isso, use a ferramenta de auto-formatação do IDE antes de começar e avance com cuidado! - As chaves precisam ser removidas, e você deve trocar cada
{
por:
no começo de um bloco de instruções (isso só não vale para as definições de arrays, que tem chaves mas não definem um bloco de instruções, e viram uma lista ou uma tupla com[ ]
ou( )
). - Remova os
;
no final das linhas. - Comentários com
//
no Java viram comentários com#
. Comentários de várias linhas com/* … */
podem virar docstrings, com aspas triplas no Python,""" … """
. - Java é uma linguagem de tipagem estática e Python é uma linguagem de tipagem dinâmica isso significa que vamos remover todas as declarações de tipo. Remova
int
,float
,String
,color
,boolean
das declarações de variáveis. Por exemplo,int i = 0;
se tornai = 0
. -
Podemos também remover
void
ou tipo na declaração de uma função e colocar no lugar odef
do Python. Depois remover a declaração de tipo dos parâmetros da função.Java
float media(float a, float b){ return (a + b) / 2; }
Python
def media(a, b): return (a + b) / 2
Um quadro com equivalências para conversão
Os valores booleanos em Java são true
e false
, o que em Python fica True
e False
. Vamos fazer um quadro com os operadores lógicos e algumas outras equivalências.
Java | Python |
---|---|
void func() { … } |
def func(): … |
true e false |
True e False |
a && b (E lógico) |
a and b |
a || b (OU lógico) |
a or b |
! a (NÃO lógico) |
not a |
i++ (incremento) |
i += 1 |
i-- (decremento) |
i -= 1 |
a <= b && b < c |
a <= b < c |
for (int i=0; i < limite; i++){ … |
for i in range(limite): … |
for (int i=inicio; i < limite; i += passo){ … |
for i in range(inicio, limite, passo): … |
for (Bola b : arrayListBolas){ … |
for b in listaBolas: … |
for (Bola b : arrayListBolas){ … |
for b in listaBolas: … |
fill(#FFCC00) // cor em notação hexadecimal |
fill("#FFCC00") # precisa aspas e não funciona com color()` |
E semelhante a null
de Java temos o valor None
em Python os usos não são totalmente equivalentes mas é um bom palpite fazer a substituição.
Loops for
O caso mais simples é um for
baseado em um contador qualquer, como for (int i=0; i < limite; i++){ …
e a tradução é for i in range(limite): …
O chamado for each, mostrado no quadro, também é muito direto, mas se você encontrar um loop for
no Java com um passo não inteiro (float), como a construção baseada em range()
no Python só funciona com números inteiros, você vai ter que implementar um range ‘especial’, como mostrado abaixo com frange()
ou então convertê-lo em um loop while
:
Java
float passo = TWO_PI / 18
for (float angulo=0; angulo < TWO_PI; angulo += passo){
…
}
Python
passo = TWO_PI / 18
angulo = 0
while angulo < TWO_PI:
…
angulo += passo
Implementando um range com passos não inteiros:
def frange(start, stop, step):
from itertools import count, takewhile
return takewhile(lambda x: x < stop, count(start, step))
# em uso no exemplo...
passo = TWO_PI / 18
for angulo in frange(0, TWO_PI, passo):
…
Aqui um exemplo de laço é feito apenas para pegar objetos de uma estrutura de dados:
for (int i = 0; i < meu_array.length; i++) {
fazendoAlgo(i, meu_array[i]);
}
Python
for item in minha_lista:
fazendoAlgo(item)
ou
for i, item in enumerate(minha_lista):
fazendoAlgo(i, item)
Veja uma iteração invertida para remover itens de um ArrayList no Java, uma lista no Python:
Java
for (int i = particles.size()-1; i >= 0; i--) {
Particle p = particles.get(i);
p.run();
if (p.isDead()) {
particles.remove(i);
}
}
Python
for i in reversed(range(len(particles))):
p = particles[i]
p.run()
if p.isDead():
particles.pop(i)
ou ainda:
for i, p in reversed(list(enumerate(particles))):
p.run()
if p.isDead():
del particles[i]
if
, else
e seus amigos
Note que a condição do if
no Python não tem os parênteses obrigatórios no Java. A combinação de um else if
vira a contração elif
.
Java
for (int i = 2; i < width-2; i += 2) {
if ((i % 20) == 0) {
stroke(255);
line(i, 80, i, height/2);
} else if ((i % 10) == 0) {
stroke(153);
line(i, 20, i, 180);
} else {
stroke(102);
line(i, height/2, i, height-20);
}
}
Python
for i in range(2, width - 2, 2):
# If 'i' divides by 20 with no remainder
if i % 20 == 0:
stroke(255)
line(i, 80, i, height / 2)
elif i % 10 == 0:
stroke(153)
line(i, 20, i, 180)
else:
stroke(102)
line(i, height / 2, i, height - 20)
Operador ternário
Java
resultado = cond ? a : b
Python
resultado = a if cond else b
switch & case
Não existia switch/case
no Python até recentementte, agora com Python 3.10 temos correspondência de padrões (pattern matching), mas acreidto que em geral você pode trocar a estrutura do Java por uma cadeia de if/elif
ou, se for só para chamar diferentes funções, um dicionário de funções [TO DO página sobre isso].
Java você pode trocar
char letter = 'b';
switch(letter) {
case 'a':
case 'A':
println("Alpha"); // Does not execute in this example
break;
case 'b':
case 'B':
println("Bravo"); // Prints "Bravo"
break;
default: // default is optional
println("Not found");
break;
}
Python
letter = 'b'
if letter == 'a' or letter == 'A':
println("Alpha") # Does not execute in this example
elif letter in ('b', 'B'):
println("Bravo") # Prints "Bravo"
else:
println("Not found")
Variáveis globais
Se a variável for declarada e inicializada (definido o tipo e o valor) no começo do sketch basta remover a declaração de tipo.
Mas como não há em Python a declaração de uma variável sem fazer uma atribuição, quando a variável é só declarada (é indicado um tipo sem a inicialização, isto é ter sua primeira atribuição) no começo do sketch precisamos ver onde ela é calculada a primeira vez e acrescentar, no início da função, a instrução global nome_da variável
.
Na verdade, toda função que altera a atribuição de variáveis globais em seu corpo precisa da instrução global
com os nomes das variáveis que são modificadas.
Veja um exemplo:
Java
int rad = 60; // Width of the shape
float xpos, ypos; // Starting position of shape
float xspeed = 2.8; // Speed of the shape
float yspeed = 2.2; // Speed of the shape
int xdirection = 1; // Left or Right
int ydirection = 1; // Top to Bottom
void setup()
{
size(600, 300);
// Set the starting position of the shape
xpos = width/2;
ypos = height/2;
}
void draw()
{
background(102);
xpos = xpos + ( xspeed * xdirection );
ypos = ypos + ( yspeed * ydirection );
if (xpos > width-rad || xpos < rad) {
xdirection *= -1;
}
if (ypos > height-rad || ypos < rad) {
ydirection *= -1;
}
ellipse(xpos, ypos, rad * 2, rad * 2);
}
Python
rad = 60; # Width of the shape
# No original tinha: float xpos, ypos; // Starting position of shape
xspeed = 2.8; # Speed of the shape
yspeed = 2.2; # Speed of the shape
xdirection = 1; # Left or Right
ydirection = 1; # Top to Bottom
def setup():**Python**
size(600, 300)
global xpos, ypos # xpos, ypos são globais criadas no setup
noStroke()
xpos = width / 2
ypos = height / 2
def draw():
global xpos, ypos, xdirection, ydirection # vão ser alteradas!
background(102)
xpos += xspeed * xdirection
ypos += yspeed * ydirection
if xpos < rad or width - rad < xpos: # note que rad não é alterada
xdirection *= -1
if ypos < rad or height - rad < ypos:
ydirection *= -1
ellipse(xpos, ypos, rad * 2, rad * 2)
Strings
Se o código contiver strings com caracteres não-ASCII (como letras acentuadas ou emojis) pode ser uma boa ideia iniciar o sketch com a seguinte linha:
from __future__ import unicode_literals
De outra forma você terá que preceder cada string com u
da seguinte maneira: u"maçã"
.
Tipo char em Java
Java tem uma tipo especial para caracteres, char que são representados no código com aspas simples, Python não tem essa distinção, usa-se strings de um só caractere, e aspas simples ou duplas para strings.
Para obter um caractere em determinada posição de string em Java é preciso fazer isto:
String palavra = "amor";
char c = palavra.charAt(1); // c = 'm'
O equivalente em Python, continua sendo string:
palavra = 'amor'
c = palavra[1] # c = 'm'
Comparando strings em Java
String str1 = "amor";
String str2 = "amor";
// Testa se str1 é igual a str2
if (str1.equals(str2)) {
println("iguais"); } else {
println("diferentes");
}
Comparando strings em Python
str1 = "amor"
str2 = "amor"
# Testa se str1 é igual a str2
if str1 == str2:
println("iguais")
else:
println("diferentes")
Importando bibliotecas e as outras abas do sketch
No Processing modo Java as bibliotecas são importadas com import
mas no modo Python essa instrução é mais usada para importar módulos da biblioteca padrão do Python, e arquivos .py das outras abas do IDE, que ao contrário do modo Java não são automaticamente parte do sketch.
Para bibliotecas de terceiros, use o comando do menu Sketch > Importar Biblioteca… (ou Sketch > Import Library… em inglês) para acrescentar a linha com add_library()
e o argumento correto.
Java
import com.hamoid.*; // importa biblioteca VideoExport no modo Java
Python
add_library('VideoExport') # a mesma biblioteca no modo Python
Para usar múltiplas abas no modo Python, é preciso tratá-las como módulos e trazer classes ou funções com import
. Há mais de uma maneira de fazer isso.
from outra_aba import * # importa código do arquivo outra_aba.py
Se as outras abas contiverem caracteres não-ASCII é necessário acrescentar como primeira linha este comentário especial:
# -*- coding: utf-8 -*-
Orientação a objetos
Obtendo uma instância e acessando métodos e atributos
Java precisa da palavra chave new
para criar uma instância de uma classe, é só removê-la! O acesso a métodos e atributos é exatamente igual.
Java
VideoExport videoExport;
void setup() {
size(600, 600);
videoExport = new VideoExport(this);
videoExport.startMovie();
}
Python
def setup() :
global videoExport
size(600, 600)
videoExport = VideoExport(this)
videoExport.startMovie()
Declarando uma classe
Já as declarações de classe mudam um pouco, grosso modo, o método __init__()
faz o papel do construtor (a definição de método que em Java tem o mesmo nome da classe e faz a inicialização de uma instância do objeto).
Você vai ter o trabalho de acrescentar self
como primeiro parâmetro de todos os métodos, e vai ter que usar self.
para acessar atributos e membros da classe.
Veja a classe MRect
do exemplo Basics > Objects > Objects que vem no IDE do Processing.
Java
class MRect
{
int w; // single bar width
float xpos; // rect xposition
float h; // rect height
float ypos ; // rect yposition
float d; // single bar distance
float t; // number of bars
MRect(int iw, float ixp, float ih, float iyp, float id, float it) {
w = iw;
xpos = ixp;
h = ih;
ypos = iyp;
d = id;
t = it;
}
void move (float posX, float posY, float damping) {
float dif = ypos - posY;
if (abs(dif) > 1) {
ypos -= dif/damping;
}
dif = xpos - posX;
if (abs(dif) > 1) {
xpos -= dif/damping;
}
}
void display() {
for (int i=0; i<t; i++) {
rect(xpos+(i*(d+w)), ypos, w, height*h);
}
}
}
Python
class MRect:
def __init__(self, iw, ixp, ih, iyp, id, it):
self.w = iw # single bar width
self.xpos = ixp # rect xposition
self.h = ih # rect height
self.ypos = iyp # rect yposition
self.d = id # single bar distance
self.t = it # number of bars
def move(self, posX, posY, damping):
self.dif = self.ypos - posY
if abs(self.dif) > 1:
self.ypos -= self.dif / damping
self.dif = self.xpos - posX
if abs(self.dif) > 1:
self.xpos -= self.dif / damping
def display(self):
for i in range(self.t):
rect(self.xpos + (i * (self.d + self.w)),
self.ypos, self.w, height * self.h)
Estruturas de dados
Arrays como int[]
, float[]
ou PVector[]
podem virar listas em Python (ou quem sabe tuplas, se forem criadas e deixadas quietas). Um ArrayList é muito parecido com uma lista:
Java
ArrayList<Bandeirinha> bandeirinhas; // uma lista de objetos da classe Bandeirinha
void setup() {
size(400, 400);
bandeirinhas = new ArrayList<Bandeirinha>();
for (int i=0; i <50; i++) {
bandeirinhas.add(new Bandeirinha(100, 100, 12));
}
}
Python
bandeirinhas = [] # uma lista de objetos Bandeirinha
def setup():
size(400, 400);
for i in range(50):
bandeirinhas.append(Bandeirinha(100, 100, 12))
ou
def setup():
global bandeirinhas
size(400, 400);
bandeirinhas = [Bandeirinha(100, 100, 12) for i in range(50)] # uma compreensão de lista de objetos Bandeirinha
Arrays 2D
Parar traduzir, arrays de duas dimensões em Java, faça uma lista de listas (não, você não pode usar numpy).
Java
int[][] board;
board = new int[grid_w][grid_h]
Python
board = [[0] * grid_w for _ in range(grid_h)]
Em vez do 0
você pode usar outro valor calculado ou None
como ‘segurador de lugar’ (placeholder) caso a estrutura vá servir para outros tipos de dados.
Trabalho em andamento…