1

I'm writing a library that parses a file, creates an object that represents the file, and allows exporting the object back to a file.

I want to validate that the required headers and columns are included any time those values are changed. Due to this, I was trying to setup validation with the @property decorator.

I noticed in the python documentation for @property they use '_variable' if the property name was 'variable'. I understand that a single underscore in front is to signify the variable is intended for weak internal use. However, I was under the impression the point of the @property decorator was that any call to set a variable would cause the setter function to run.

_headers = None

required_headers = ['FIELD_DELIM', 'VIDEO_FORMAT', 'FPS']

@property
def headers(self):
    return self._headers

@headers.setter
def headers(self, value):
    for header in self.required_headers:
        if header not in value:
            raise Exception
    self._headers = value

While this code works, I know that I can still bypass my setter by doing myObject._headers=value.

Is there a way I can ensure that validation is always performed without relying on a user to respect _headers is for internal use?

K Engle
  • 1,322
  • 2
  • 11
  • 23
  • 2
    As a side note, `_headers = None` is setting a _class_ attribute, one shared by all instances of the class, not an _instance_ attribute. A `@property`, on the other hand, is for per-instance values. So, your code as written is misleading at best. – abarnert Sep 26 '14 at 19:10
  • I took out the _headers = None from my class, it really wasn't needed. Thanks for the tip. – K Engle Sep 26 '14 at 19:19

4 Answers4

7

Python is not designed to help you "ensure" that nobody misuses your objects like that. The underscore prefix for private attributes, and the @property mechanism for hiding attributes behind getters and setters, can ensure that it's obvious that people shouldn't use your objects like that, and make it harder for them to do so accidentally, but it can't prevent them from actually doing so maliciously.

While there are tricks you can use to hide your attributes even better, in a highly dynamic, introspectable language like Python, there's always going to be a way to get around that—look directly in your __dict__, or in whatever other attribute you hide them in, or just change the __class__ of your object to something less restrictive.

In other words, you already can rely on a user to respect that _headers is for internal use if all you're worried about is your users being idiots; if you're worried about them being malicious, Python is the wrong language for you.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • I was mainly concerned with people looking at my source, not understanding what @property is, and then using the _headers version instead. Thanks. – K Engle Sep 26 '14 at 19:22
  • 2
    @KEngle That's what doc strings are for. :) – Silas Ray Sep 26 '14 at 19:39
  • 2
    @KEngle: If you're writing code to be used by novices, and expecting them to use the code as its own docs, you may well want to add some extra comments, like `self._headers = None # don't set this directly in your code, use the spam.header property` or similar, as well as a doc string listing the public attributes (whether real or `@property`). – abarnert Sep 26 '14 at 19:40
  • 1
    @SilasRay: Thanks for getting there before me, and more concisely. :) – abarnert Sep 26 '14 at 19:41
  • 1
    @KEngle: Also, note that many introspection features will leave out underscore-prefixed attributes by default, which helps. For example, if I fire up IPython, `import` your class, create an instance, and write `instance.` and then use tab completion, it will show me `headers` but not `_headers`. – abarnert Sep 26 '14 at 19:42
2

You can use double underscore for name mangling or implement a custom descriptor, but one of Python's core tenets is that users are expected to be "consenting adults" who respect interfaces and do their best not to do things that break interfaces without a very good reason. Basically, don't worry about it and just use the single underscore to store the data on the object.

Silas Ray
  • 25,682
  • 5
  • 48
  • 63
1

No, Python doesn't enforce the concept of private vs public like Java does

DevLounge
  • 8,313
  • 3
  • 31
  • 44
  • Python does have the concept of private vs. public; that's what the underscore prefix is. It just doesn't try to _enforce_ that concept the way that, say, Java does. – abarnert Sep 26 '14 at 19:11
  • 1
    Also, your simplified version doesn't simplify things, it changes the logic completely. He's asking if any of his `required_headers` is a _substring_ of `value`, you're asking if any of them _equal_ `value`. – abarnert Sep 26 '14 at 19:12
  • I know, but one can always access the obj.__dict__ or whatever underscored variable / member – DevLounge Sep 26 '14 at 19:13
  • Yes, that's exactly what "doesn't try to enforce that concept the way that, say, Java does" means. It still _has_ the concept. – abarnert Sep 26 '14 at 19:21
1

sort of. there's no real privacy in python and with a little work a user can circumvent your privacy safegauards.

if you want, you could implement __getattribute__ which checks any time you try to access an element of your class, but even that's not foolproof. check out this link Difference between __getattr__ vs __getattribute__

Community
  • 1
  • 1
acushner
  • 9,595
  • 1
  • 34
  • 34