1

I'm not so much familiarized with Python classes, but I need to detect changes of instance variables of a given class. According to the documentation, true "private" instance variables do not exist, so in particular I want to detect changes when the instance variable is accessed directly not through a specific method. For example:

    class myclass:
       def __init__(self):
           self.var1 = []

       def addelement(self, data):
           self.var1.append(data)
           print('element',data,'added')

    a=myclass()
    a.addelement(6)

    "element 6 added"
    print(a.var1)
    [6]

    a.var1.append(7)
    a.var1
    [6, 7]

In the later case I don't know how to detect the added data. I now that methods can be overriding but I would like to know if there is a general approach that allows detecting changes whatever be the variable nature (dict, list, string...). Thank you.

4
  • You could override __getattribute__ for your class to define what a.var1 means, but an adversary could still access the instance variable directly with a.__dict__['var1'] instead of a.var1. You simply cannot do what you want. Commented Oct 23, 2017 at 11:34
  • @chepner I forgot about __getattribute__, I think that's a better answer than mine. Commented Oct 23, 2017 at 11:37
  • 1
    @chepner __getattribute__ can prevent you from accessing a.__dict__. It's still not possible to prevent access, you can still e.g. access super(type(a), a).__getattribute__('var1'). Commented Oct 23, 2017 at 11:40
  • __getattribute__ is probably overkill; I mentioned it simply because even it can be bypassed (although in a different way than I mentioned, thanks L3viathan). I upvoted your answer for the mention that Python simply doesn't provide privacy protection. Commented Oct 23, 2017 at 11:41

2 Answers 2

2

The closest thing to what you want is a property. Try to run:

#!/usr/bin/python3

class bla:
    @property 
    def myfield(self):
        return self._myhiddenfield
    @myfield.setter
    def myfield(self, somethingelse):
        print('Oh no! The enemy changed my field!')
        self._myhiddenfield = somethingelse

bla().myfield = 3

myfield behaves like a field that whenever set will warn you on the screen. You can of course do whatever you want. You can decide to not touch the hidden field if you prefer.

Please note, if the enemy (don't know why you have one) still accesses _myhiddenfield you are screwed. The _* name is a common practice in Python to hint "don't mess with this" (thanks @chepner), but in Python there is no enforcement of privacy, so this is the best you have. I guess you could obfuscate some more.

Just to complete the picture, you can define a getter as well - to warn you when the field is being read.

In my example myfield is a property. You should google that, the @ notation which is a decorator, and setters and getters.

One final note. In your example, you are not changing myclass, you are changing the list, and using append rather than setting a field directly. In this case you would have to override append and create your own list, as there is no inner variable (as far as you're concerned) being touched. There is no way as far as I know to detect that - it even bypasses Python mutability definitions (in a sense).

Sign up to request clarification or add additional context in comments.

3 Comments

_* is the "treat this as private" convention; __*__ invokes name munging to help prevent inadvertent shadowing when subclassing a function.
@chepner thanks for clarifying, I'll amend the post.
Actually _<something> is the convention for "protected", and __<magic>__ for "magic" methods & attributes (methods that allow operators overloading etc) - note that __<magic>__ names are reserved by the language. The naming for "private" (name-mangling) methods / attributes is __<something>
1

Use __setattr__ and __delattr__ on your class.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.