Qual é o equivalente idiomático do Python desse código C / C ++?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
especificamente, como se implementa o membro estático no nível da função, em oposição ao nível da classe? E colocar a função em uma classe muda alguma coisa?
_
prefixo convencional .Respostas:
Um pouco invertido, mas isso deve funcionar:
Se você deseja o código de inicialização do contador na parte superior e não na inferior, é possível criar um decorador:
Em seguida, use o código como este:
Ainda será necessário que você use o
foo.
prefixo, infelizmente.(Crédito: @ony )
fonte
if "counter" not in foo.__dict__: foo.counter = 0
como as primeiras linhas defoo()
. Isso ajudaria a evitar código fora da função. Não tenho certeza se isso era possível em 2008. PS acharam esta resposta enquanto procura possibilidade de criar variáveis de função estáticos, por isso esta discussão ainda está "vivo" :)foo
efoo.counter =
está intimamente relacionado. no entanto, prefiro a abordagem do decorador, já que não há como o decorador não ser chamado e é semanticamente mais óbvio o que ele faz (@static_var("counter", 0)
é mais fácil e faz mais sentido para os meus olhos do queif "counter" not in foo.__dict__: foo.counter = 0
, especialmente porque no último você deve usar o nome da função (duas vezes) que pode mudar).def foo():
if not hasattr(foo,"counter"): foo.counter=0
foo.counter += 1
Você pode adicionar atributos a uma função e usá-lo como uma variável estática.
Como alternativa, se você não deseja configurar a variável fora da função, pode usar
hasattr()
para evitar umaAttributeError
exceção:De qualquer forma, as variáveis estáticas são bastante raras e você deve encontrar um local melhor para essa variável, provavelmente dentro de uma classe.
fonte
try: myfunc.counter += 1; except AttributeError: myfunc.counter = 1
deve fazer o mesmo, usando exceções.try
adiciona algum custo? Apenas curioso.Pode-se também considerar:
Raciocínio:
if
ramificação (pense na exceção StopIteration )fonte
def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
hasattr()
para isso não é mais simples e também menos eficiente.Outras respostas demonstraram a maneira como você deve fazer isso. Aqui está uma maneira que você não deve:
Os valores padrão são inicializados apenas quando a função é avaliada pela primeira vez, não sempre que é executada, para que você possa usar uma lista ou qualquer outro objeto mutável para armazenar valores estáticos.
fonte
def foo(arg1, arg2, _localstorage=DataClass(counter=0))
eu acho bem legível. Outro ponto positivo é a renomeação fácil da função.types.SimpleNamespace
lo,def foo(arg1, arg2, _staticstorage=types.SimpleNamespace(counter=0)):
sem precisar definir uma classe especial.Muitas pessoas já sugeriram testar o 'hasattr', mas há uma resposta mais simples:
Nenhuma tentativa / exceto, nenhum teste de hasattr, apenas getattr com um padrão.
fonte
try
/except
baseada em ravwojdyla é praticamente sem sentido. Uma simplesipython
%%timeit
marca de microbench deu o custo detry
/except
a 255 ns por chamada, contra 263 ns para agetattr
solução baseada. Sim, otry
/except
é mais rápido, mas não é exatamente "ganhar as mãos para baixo"; é uma pequena micro-otimização. Escreva o código que parecer mais claro, não se preocupe com diferenças triviais de desempenho como esta.Aqui está uma versão totalmente encapsulada que não requer uma chamada de inicialização externa:
No Python, funções são objetos e podemos simplesmente adicionar, ou aplicar um patch de macaco, variáveis de membro a elas através do atributo especial
__dict__
. O internovars()
retorna o atributo especial__dict__
.EDIT: Observe que, diferentemente da
try:except AttributeError
resposta alternativa , com essa abordagem a variável estará sempre pronta para a lógica do código após a inicialização. Eu acho que atry:except AttributeError
alternativa para o seguinte será menos SECA e / ou terá um fluxo estranho:EDIT2: Eu apenas recomendo a abordagem acima quando a função será chamada de vários locais. Se, em vez disso, a função for chamada apenas em um lugar, é melhor usar
nonlocal
:fonte
try: mystaticfun.counter+=10 except AttributeError: mystaticfun.counter=0
X not in Y
em vez denot X in Y
(ou aconselhar usar se você foram apenas usá-lo por causa de uma comparação mais olhando semelhante-entre isso ehasattr
)def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
O Python não possui variáveis estáticas, mas você pode falsificá-lo definindo um objeto de classe que pode ser chamado e, em seguida, usando-o como uma função. Veja também esta resposta .
Observe que
__call__
faz com que uma instância de uma classe (objeto) possa ser chamada com seu próprio nome. É por isso que chamarfoo()
acima chama o__call__
método da classe A partir da documentação :fonte
foo
que é fornecida como um singleton.Use uma função de gerador para gerar um iterador.
Então use-o como
Se você deseja um limite superior:
Se o iterador terminar (como no exemplo acima), você também poderá fazer um loop direto sobre ele, como
Obviamente, nesses casos simples, é melhor usar o xrange :)
Aqui está a documentação na declaração de rendimento .
fonte
Outras soluções anexam um atributo de contador à função, geralmente com lógica complicada para lidar com a inicialização. Isso é inadequado para o novo código.
No Python 3, o caminho certo é usar uma
nonlocal
declaração:Veja PEP 3104 para a especificação da
nonlocal
declaração.Se o contador se destina a ser privado para o módulo, ele deve ser nomeado
_counter
.fonte
global counter
instrução em vez denonlocal counter
(nonlocal
apenas permite escrever no estado de fechamento em uma função aninhada). O motivo pelo qual as pessoas estão anexando um atributo à função é evitar poluir o espaço de nomes global para o estado específico da função, para que você não precise fazer coisas ainda mais obscenas quando duas funções precisam decounter
s independentes . Essa solução não é escalável; atributos na função do. A resposta do kdb é comononlocal
pode ajudar, mas adiciona complexidade.nonlocal
maisglobal
é exatamente como você apontar - ele funciona em estritamente mais circunstâncias.O uso de um atributo de uma função como variável estática tem algumas desvantagens em potencial:
O python linguístico para o segundo problema provavelmente nomeará a variável com um sublinhado principal para sinalizar que ela não deve ser acessada, mantendo-a acessível após o fato.
Uma alternativa seria um padrão usando fechamentos lexicais, suportados pela
nonlocal
palavra - chave no python 3.Infelizmente, não sei como encapsular esta solução em um decorador.
fonte
Assim como o código de vincent acima, isso seria usado como decorador de função e as variáveis estáticas devem ser acessadas com o nome da função como prefixo. A vantagem desse código (embora seja certo que alguém possa ser inteligente o suficiente para descobrir isso) é que você pode ter várias variáveis estáticas e inicializá-las de uma maneira mais convencional.
fonte
Um pouco mais legível, mas mais detalhado (Zen of Python: explícito é melhor que implícito):
Veja aqui uma explicação de como isso funciona.
fonte
foo()
deve reinicializar o dicionário para o valor especificado na definição da função (portanto, com a tecla do contador com o valor 0). Por que não?O Python costuma usar sublinhados para indicar variáveis privadas. O único motivo em C para declarar a variável estática dentro da função é ocultá-la fora da função, o que não é realmente Python idiomático.
fonte
Depois de tentar várias abordagens, acabo usando uma versão aprimorada da resposta do @ warvariuc:
fonte
A maneira idiomática é usar uma classe , que pode ter atributos. Se você precisar que as instâncias não sejam separadas, use um singleton.
Existem várias maneiras de falsificar ou mover variáveis "estáticas" para o Python (uma que não foi mencionada até agora é ter um argumento padrão mutável), mas essa não é a maneira idiomática Pythonic de fazer isso. Basta usar uma classe.
Ou possivelmente um gerador, se o seu padrão de uso se encaixar.
fonte
default
argumento é o mais elegante.Solicitado por esta pergunta , posso apresentar outra alternativa que pode ser um pouco mais agradável de usar e terá a mesma aparência para métodos e funções:
Se você gosta do uso, aqui está a implementação:
fonte
Outra reviravolta (não recomendada!) No objeto que pode ser chamado, como https://stackoverflow.com/a/279598/916373 , se você não se importa em usar uma assinatura de chamada descolada, seria
fonte
Em vez de criar uma função com uma variável local estática, você sempre pode criar o que é chamado de "objeto de função" e fornecer uma variável de membro padrão (não estática).
Como você deu um exemplo escrito em C ++, primeiro explicarei o que é um "objeto de função" em C ++. Um "objeto de função" é simplesmente qualquer classe com uma sobrecarga
operator()
. Instâncias da classe se comportarão como funções. Por exemplo, você pode escreverint x = square(5);
mesmo quesquare
seja um objeto (com sobrecargaoperator()
) e tecnicamente não seja uma "função". Você pode dar a um objeto de função qualquer um dos recursos que você poderia dar a um objeto de classe.No Python, também podemos sobrecarregar,
operator()
exceto que o método é chamado__call__
:Aqui está uma definição de classe:
Aqui está um exemplo da classe que está sendo usada:
A saída impressa no console é:
Se você deseja que sua função receba argumentos de entrada, também pode adicioná-los
__call__
:fonte
Soulution n + = 1
fonte
Uma declaração global fornece essa funcionalidade. No exemplo abaixo (python 3.5 ou superior para usar o "f"), a variável counter é definida fora da função. Definir como global na função significa que a versão "global" fora da função deve ser disponibilizada para a função. Portanto, cada vez que a função é executada, ela modifica o valor fora da função, preservando-o além da função.
fonte
Uma variável estática dentro de um método Python
fonte
Pessoalmente, prefiro o seguinte aos decoradores. Cada um com sua mania.
fonte
Esta resposta baseia-se na resposta de @claudiu.
Eu descobri que meu código estava ficando menos claro quando eu sempre precisava acrescentar o nome da função, sempre que pretendia acessar uma variável estática.
Ou seja, no meu código de função eu preferiria escrever:
ao invés de
Então, minha solução é:
statics
atributo à funçãostatics
como um alias paramy_function.statics
Observação
Meu método usa uma classe chamada
Bunch
, que é um dicionário que suporta acesso ao estilo de atributo, à la JavaScript (consulte o artigo original sobre isso, por volta de 2000)Pode ser instalado via
pip install bunch
Também pode ser manuscrito da seguinte maneira:
fonte
types.SimpleNamespace
(disponível desde 3.3) suporta esse comportamento imediatamente (e é implementado em C no CPython, por isso é o mais rápido possível).Com base na resposta de Daniel (adições):
A razão pela qual desejei adicionar esta parte é que as variáveis estáticas são usadas não apenas para incrementar por algum valor, mas também para verificar se o var estático é igual a algum valor, como um exemplo da vida real.
A variável estática ainda está protegida e usada apenas dentro do escopo da função use_foo ()
Neste exemplo, chame as funções foo () exatamente como (com relação ao equivalente correspondente em c ++):
se a classe Foo for definida restritamente como uma classe singleton, isso seria o ideal. Isso tornaria mais pitônico.
fonte
Claro que essa é uma pergunta antiga, mas acho que posso fornecer alguma atualização.
Parece que o argumento de desempenho é obsoleto. O mesmo conjunto de testes parece fornecer resultados semelhantes para siInt_try e isInt_re2. Obviamente, os resultados variam, mas esta é uma sessão no meu computador com o python 3.4.4 no kernel 4.3.01 com o Xeon W3550. Já o executei várias vezes e os resultados parecem semelhantes. Mudei o regex global para a função estática, mas a diferença de desempenho é insignificante.
Com o problema de desempenho fora do caminho, parece que o try / catch produziria o código mais à prova de futuro e de base de milho, portanto, talvez apenas o envolva na função
fonte