Pergunta

Eu tenho alguns ctypes ligações, e para cada body.New eu deveria chamar body.Free. A biblioteca Estou vinculativo não tem rotinas de alocação de isolamento para fora do resto do código (que pode ser chamado em qualquer lugar lá), e para acoplar o uso de recursos úteis que eu preciso fazer referências cíclicas.

Eu acho que ele iria resolver se eu gostaria de encontrar uma maneira confiável para ligar destructor a um objeto. (Weakrefs ajudaria se eles me dão o retorno apenas antes os dados são descartados.

Então, obviamente, este megafails código quando eu colocar em velocity_func:

class Body(object):
    def __init__(self, mass, inertia):
        self._body = body.New(mass, inertia)

    def __del__(self):
        print '__del__ %r' % self
        if body:
            body.Free(self._body)

    ...        

    def set_velocity_func(self, func):
        self._body.contents.velocity_func = ctypes_wrapping(func)

Eu também tentei resolvê-lo através weakrefs, com essas coisas parecem ficar apenas pior, só apenas em grande parte mais imprevisível.

Mesmo se eu não colocar no velocity_func, aparecerá ciclos, pelo menos, em seguida, quando eu faço isso:

class Toy(object):
    def __init__(self, body):
        self.body.owner = self

...

def collision(a, b, contacts):
    whatever(a.body.owner)

Assim como se certificar de Estruturas terá lixo coletado, mesmo se eles são alocados / liberados pela biblioteca compartilhada?

Há repositório se você estiver interessado sobre mais detalhes: http://bitbucket.org/cheery/ ctypes-esquilo /

Foi útil?

Solução 3

Se weakrefs não estão quebrados, eu acho que isso pode funcionar:

from weakref import ref

pointers = set()

class Pointer(object):
    def __init__(self, cfun, ptr):
        pointers.add(self)
        self.ref = ref(ptr, self.cleanup)
        self.data = cast(ptr, c_void_p).value # python cast it so smart, but it can't be smarter than this.
        self.cfun = cfun

    def cleanup(self, obj):
        print 'cleanup 0x%x' % self.data
        self.cfun(self.data)
        pointers.remove(self)

def cleanup(cfun, ptr):
    Pointer(cfun, ptr)

Eu ainda tentar. A peça importante é que o ponteiro não tem quaisquer referências fortes para o ponteiro estrangeira, exceto um inteiro. Isso deve funcionar se ctypes não faz memória livre que eu deveria libertar com as ligações. Sim, é basicamente um hack, mas eu acho que pode funcionar melhor do que as coisas anteriores eu tenho tentado.

Edit: Tentou fazê-lo, e ele parece funcionar após pequena finetuning meu código. A coisa surpreendente é que, mesmo se eu tenho del para fora de todos os meus estruturas, que parecem ainda falhar. Interessante, mas frustrante.

Nem os trabalhos, de algum acaso estranho que eu tenho sido capaz de cair fora referências cíclicas em alguns lugares, mas as coisas ficar sem dinheiro.

Edit: Bem .. weakrefs foram quebrados depois de tudo! então não há provavelmente nenhuma solução para limpeza de confiança em python, com exceção de forçá-lo a ser explícito.

Outras dicas

O que você quer fazer, que é criar um objeto que aloca coisas e depois desaloca automaticamente quando o objeto não está mais em uso, é quase impossível em Python, infelizmente. O del afirmação não é garantido para ser chamado, para que você não pode contar com isso.

A forma padrão em Python é simplesmente:

try:
    allocate()
    dostuff()
finally:
    cleanup()

Ou desde 2.5 você também pode criar o contexto-gestores e usar o com a declaração, que é uma maneira mais limpa de fazer isso.

Mas ambos são primariamente para quando você alocar / lock no início de um trecho de código. Se você quiser ter coisas alocados para todo o prazo do programa, você precisa alocar o recurso na inicialização, antes do código principal do programa é executado, e desalocar depois. Há uma situação que não é coberto aqui, e isso é quando você deseja alocar e desalocar muitos recursos de forma dinâmica e usá-los em muitos lugares no código. Por exemplo você quer uma piscina de buffers de memória ou similar. Mas a maioria desses casos são de memória, que Python vai lidar para você, assim você não precisa se preocupar com aqueles. Há naturalmente dos casos em que você quer ter alocação piscina dinâmica de coisas que não são memória, e então você iria querer o tipo de deallocation tentar no seu exemplo, e que é complicada de fazer com Python .

Em CPython, __del__ é um destruidor de confiança de um objeto, porque ele vai ser sempre chamado quando os alcances contagem de referência zero (nota: pode haver casos - como referências circulares de itens com método __del__ definido -., onde a contagem de referência nunca vai chegar a zero, mas isso é outra questão)

Atualizar A partir dos comentários, eu entendo o problema está relacionado com a ordem de destruição de objetos: corpo é um objeto global, e isso está sendo destruído antes de todos os outros objetos, portanto, não está mais disponível para eles.
Na verdade, usando objetos globais não é bom; não só por causa de problemas como este, mas também por causa da manutenção.

Eu, então, mudar a sua classe com algo como isto

class Body(object):
    def __init__(self, mass, inertia):
        self._bodyref = body
        self._body = body.New(mass, inertia)

    def __del__(self):
        print '__del__ %r' % self
        if body:
            body.Free(self._body)

...        

def set_velocity_func(self, func):
    self._body.contents.velocity_func = ctypes_wrapping(func)

Um par de notas:

  1. A alteração só está acrescentando uma referência ao global corpo objeto, que, assim, vai viver, pelo menos tanto quanto todos os objetos derivado dessa classe.
  2. Ainda assim, usando um objeto global não é bom por causa do teste de unidade e de manutenção; melhor seria ter uma fábrica para o objeto, que irá definir o "corpo" correto para a classe, e em caso de teste de unidade será fácil colocar um objeto fictício. Mas isso é realmente até você e quanto esforço você acha que faz sentido neste projeto.
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top