2017-09-25 4 views
0

J'ai essayé d'ajouter des diagnostics à une structure ctypes imbriquée, mais je n'ai pas réussi à le faire et j'aimerais en connaître la raison. Un exemple bare-bones pour ce qui fonctionne comme prévu:Impossible d'ajouter un attribut à une structure imbriquée dans ctypes

import ctypes 

class FirstStruct(ctypes.Structure): 
    _fields_ = [('ch', ctypes.c_ubyte)] 

f = FirstStruct() 

print type(f) 
print hasattr(f, 'helper') 
f.helper = 'xyz' 
print hasattr(f, 'helper') 

Ces lignes imprimer ce que je pensais:

<class '__main__.FirstStruct'> 
False 
True 

Mais quand je l'utiliser dans une autre structure, il échoue:

class SecondStruct(ctypes.Structure): 
    _fields_ = [('first', FirstStruct)] 

s = SecondStruct() 

print type(s.first) 
print hasattr(s.first, 'helper') 
s.first.helper = 'xyz' 
print hasattr(s.first, 'helper') 

Les résultats ci-dessus dans

<class '__main__.FirstStruct'> 
False 
False 

Quelqu'un peut-il m'expliquer s'il vous plaît la différence? (. Je courais sur Python 2.7.8 Rappelez-vous, je ne veux pas changer la structure elle-même, mais je voulais ajouter une variable supplémentaire en dehors de la structure ctypes.)


EDIT:

est ici un exemple plus direct:

import ctypes 

class FirstStruct(ctypes.Structure): 
    _fields_ = [('ch', ctypes.c_ubyte)] 

class SecondStruct(ctypes.Structure): 
    _fields_ = [('first', FirstStruct)] 

f = FirstStruct() 
s = SecondStruct() 

f.helper = 'aaa' 
s.first.helper = 'bbb' 
s.first.ch = 0 
t = s.first 
t.helper = 'ccc' 
t.ch = 12 

print f.helper   # aaa 
print t.ch    # 12 
print s.first.ch  # 12 
print t.helper   # ccc 
print s.first.helper # AttributeError: 'FirstStruct' object has no attribute 'helper' 

Les questions sont: pourquoi ne pas s.first et t équivalent, et pourquoi ne pas s.first.helper déclenchement d'un avertissement si je ca pas mis après tout?

+1

La réponse courte est le ctype.Structures ne sont pas mutables. Les affectations d'objets mutables pointent vers le même emplacement de mémoire que la source. Avec les structures, une nouvelle valeur est créée, même si le type est conservé (FirstStruct). Cela signifie que changer la source ne modifie pas les variables assignées. Les dictionnaires pourraient faire l'affaire pour vous. Avez-vous essayé d'utiliser l'a dict comme le conteneur le plus haut niveau? –

+0

@ RonNorris Désolé, je comprends presque ce que vous avez écrit ... Mais pas encore. Essayé le concept avec la réponse https://stackoverflow.com/a/4828831/501814 pour un objet immuable, mais ici, je reçois une erreur d'attribut lors de la tentative d'attribution d'un nouvel attribut (juste ce que je m'attendrais de s.first si il est vraiment immuable.) Est une version mutable de l'immutable s.first, pointant vers le même emplacement de mémoire ctypes mais d'un endroit différent où il a l'attribut 'helper', alors que la version altérée de s.first est immédiatement perdue parce que de l'immutabilité de s? – Andris

+1

À peu près. 's.helper = 'sss'' fonctionnera exactement comme avec' t.helper = 'ccc'' car vous ne jouez pas avec l'objet original FirstStruct - vous ajoutez un attribut à' s '. –

Répondre

0

Dans votre deuxième exemple, s.first renvoie une copie de la structure interne. Vous pouvez le voir en regardant son id():

>>> id(s.first) 
112955080L 
>>> id(s.first) # didn't change 
112955080L 
>>> f=s.first # capture a reference the object returned 
>>> id(f)  # still the same 
112955080L 
>>> id(s.first) # changed! 
113484232L 

Ce qui se passait était la nouvelle copie d'être renvoyé sans cesse été affecté à la même adresse, mais immédiatement libéré. Après qu'une référence a été faite, la copie est à une autre adresse.

Donc vous êtes en créant un attribut helper, juste sur un objet temporaire.

Dans votre premier exemple, f fait directement référence à l'instance FirstStruct, ce qui vous permet de définir et de lire l'attribut.

+0

Merci. Un ajout: la structure ctypes sous-jacente reste la même - donc ctypes.addressof (f) == ctypes.addressof (s.first), comme indiqué par la [ligne de documentation] finalement trouvée (https://docs.python.org/ 2/library/ctypes.html # surprises) _ "Gardez à l'esprit que la récupération des sous-objets de Structure, Unions et Arrays ne copie pas le sous-objet, mais récupère un objet wrapper accédant au tampon sous-jacent de l'objet racine. "_ – Andris

0

Si vous utilisez le module de copie, vous pouvez obtenir un instantané actuel de l'objet ctype que vous créez. Essayez donc:

import copy 
import ctypes 

class FirstStruct(ctypes.Structure): 
    _fields_ = [('ch', ctypes.c_ubyte)] 

f = FirstStruct() 

print type(f) 
print hasattr(f, 'helper') 
f.helper = 'xyz' 
print hasattr(f, 'helper') 

t = copy.copy(f) 
print hasattr(t, 'helper') 
>>> True 
+0

Peut-être que mon exemple était trop abstrait. J'ai un arbre énorme de telles structures et certaines valeurs dans une branche de celui-ci modifie la façon dont l'autre partie doit être décodée. Par conséquent, je veux ajouter un drapeau à FirstStruct alors qu'il se trouve dans SecondStruct. (Pas à une copie de celui-ci.) – Andris