Pergunta

Eu quero peça um mímico de código C em Python com ctypes, o código é algo como:

typedef struct {
  int x;
  int y;
} point;

void copy_point(point *a, point *b) {
  *a = *b;
}

em ctypes não é possível fazer o seguinte:

from ctypes import *

class Point(Structure):
  _fields_ = [("x", c_int),("y", c_int)]

def copy_point(a, b):
  a.contents = b.contents

p0 = pointer(Point())
p1 = pointer(Point())
copy_point(p0,p1)

como o contents ainda é um Python ctypes objeto de estrutura, que é gerido como uma referência em si.

Uma solução óbvia seria a de copiar manualmente cada campo (que é representado como python imutável int do), mas isso não escala com estruturas mais complexas. Além disso, ele precisaria ser feito de forma recursiva para campos que não são básicos, mas tipos estruturados.

A minha outra opção é usar memmove e copiar os objetos como se fossem buffers, mas que parece muito propenso a erros (como Python é digitado dinamicamente seria muito fácil de usá-lo com objetos do tipo e tamanho distintos, levando a corrupção de memória ou falhas de segmentação) ...

Todas as sugestões?

Editar :

Eu também poderia usar uma nova cópia fresca da estrutura, então talvez isso poderia ser útil:

import copy
p0 = Point()
p1 = copy.deepcopy(p0) #or just a shallow copy for this example

mas eu não sei se pode haver algum tipo de comportamentos bizarros copiar ctypes proxys como se fossem regulares Python objetos ...

Foi útil?

Solução

Você pode usar a atribuição sequência para copiar o pontas-de objetos (em vez de atribuir a p.contents, que altera o valor do ponteiro):

def copy(dst, src):
    """Copies the contents of src to dst"""
    pointer(dst)[0] = src

# alternately
def new_copy(src):
    """Returns a new ctypes object which is a bitwise copy of an existing one"""
    dst = type(src)()
    pointer(dst)[0] = src
    return dst

# or if using pointers
def ptr_copy(dst_ptr, src_ptr):
    dst_ptr[0] = src_ptr[0]

ctypes vai fazer verificação de tipo para você (que não é à prova de idiota, mas é melhor que nada).

Exemplo de uso, com a verificação de que ele faz no trabalho de fato;):

>>> o1 = Point(1, 1)
>>> o2 = Point(2, 2)
>>> print (o1.x, o1.y, addressof(o1)), (o2.x, o2.y, addressof(o2))
(1, 1, 6474004) (2, 2, 6473524)
>>> copy(o2, o1)
>>> pprint (o1.x, o1.y, addressof(o1)), (o2.x, o2.y, addressof(o2))
(1, 1, 6474004) (1, 1, 6473524)

>>> o1 = Point(1, 1), o2 = Point(2, 2)
>>> print (o1.x, o1.y, addressof(o1)), (o2.x, o2.y, addressof(o2))
(1, 1, 6473844) (2, 2, 6473684)
>>> p1, p2 = pointer(o1), pointer(o2)
>>> addressof(p1.contents), addressof(p2.contents)
(6473844, 6473684)
>>> ptr_copy(p1, p2)
>>> print (o1.x, o1.y, addressof(o1)), (o2.x, o2.y, addressof(o2))
(2, 2, 6473844) (2, 2, 6473684)
>>> addressof(p1.contents), addressof(p2.contents)
(6473844, 6473684)

Outras dicas

memmove é a operação correta aqui. Ao definir o argtypes da sua função CopyPoint, você pode facilmente aplicar tipo de segurança.

from ctypes import *

class Point(Structure):
    _fields_ = [("x", c_int), ("y", c_int)]
    def __str__(self):
        return "<Point: x=%d, y=%d, addr=%ld>" % (self.x, self.y, addressof(self))

def CopyPoint(a, b):
    memmove(a, b, sizeof(Point))
CopyPoint.argtypes = [POINTER(Point), POINTER(Point)]

pt0 = Point(x=0, y=10)
pt1 = Point(x=5, y=7)

print pt0, pt1

CopyPoint(byref(pt0), byref(pt1))
print pt0, pt1    

try:
    CopyPoint(byref(pt0), Point(x=2, y=3))
except ArgumentError as e:
    print "Could not copy!", e

saídas:

$ python ct.py 
<Point: x=0, y=10, addr=3083711192> <Point: x=5, y=7, addr=3083711120>
<Point: x=5, y=7, addr=3083711192> <Point: x=5, y=7, addr=3083711120>
Could not copy! argument 2: <type 'exceptions.TypeError'>: wrong type

Note que você poderia facilmente fazer uma fábrica para gerar este tipo de função em tempo de execução com base em um tipo específico, se você precisa para generalizar:

def CopierFactory(typ):
    def f(a,b):
        memmove(a,b, sizeof(typ))
    f.argtypes = [POINTER(typ), POINTER(typ)]

    return f

copy_point = CopierFactory(Point)

a = Point(x=1, y=2)
b = Point(x=-1, y=-1)
print a, b
copy_point(byref(a), byref(b))
print a, b

saída:

<Point: x=1, y=2, addr=3085088024> <Point: x=-1, y=-1, addr=3085087952>
<Point: x=-1, y=-1, addr=3085088024> <Point: x=-1, y=-1, addr=3085087952>

operações de ponteiro como regra não são seguros memória muito. Gostaria de criar classes de mensagens publicitárias para o tipo de dados de cada struct você está interessado em e deixá-los lidar com as operações de cópia ponteiro. Muito bonito como você está fazendo aqui. Há lambda e mapear funções que você pode usar de forma recursiva como açúcar sintático.

Agora estou também a pensar sobre a definição de um método como:

def safe_copy(dst, src):
  if type(src) != type(dst) or not isinstance(src, Structure):
    raise Exception("wrong types")
  memmove(addressof(dst), addressof(src), sizeof(src))

Mas pode haver opções ainda mais agradáveis ??lá fora ...

Em python 3x, o código pode ser executado corretamente. mostrado abaixo:

>>> from ctypes import *
>>> class Point(Structure):
...   _fields_ = [("x", c_int),("y", c_int)]
>>> def copy_point(a, b):
...   a.contents = b.contents
>>> p0 = pointer(Point())
>>> p1 = pointer(Point(1,2))
>>> p0.contents.x
0
>>> copy_point(p0,p1)
>>> p0.contents.x
1
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top