I recently started using UtilSnips, a Vim plugin allowing for a certain level of automation while coding by using template-like code snippets for common code fragments (class and function definitions, for example).
When using a popular snippet pack with Python, I noticed that snippets used when writing a class use a pattern I've never seen before in __init__
when using more arguments than just self
:
class MyClass(object):
"""Docstring for MyClass. """
def __init__(self, user, password):
"""TODO: to be defined.
:user: TODO
:password: TODO
"""
self._user = user # this is the pattern I'm unfamiliar with
self._password = password
Is it common (or good) practice to use underscore when assigning instance variables in a constructor for a class in Python, or is this likely just the snippet pack author's preference? I haven't seen this type of naming convention in any other Python code I've interacted with.
-
4See PEP 8: one leading underscore marks a field as private by convention, though it does not actually hide the field.amon– amon2016年02月11日 15:11:59 +00:00Commented Feb 11, 2016 at 15:11
1 Answer 1
Leading Underscores
A leading underscore means "hands off" to the user, though an advanced user may expect to use it in certain circumstances, such as when subclassing your object.
Usage demonstration with property decorator
We tend to use underscores when using a property, for example:
class MyClass(object):
def __init__(self, user, password):
self._user = user
self._password = password
@property
def user(self):
return self._user
# The rest are optional - leaving off has same behavior
# but this demonstrates usage.
@user.setter
def user(self, value):
"""optional, raise a custom AttributeError message"""
raise AttributeError('do not attempt to modify user member')
@user.deleter
def user(self):
"""optional, raise a custom AttributeError message"""
raise AttributeError('do not attempt to modify user member')
So one using your code can create the object, but when attempting to modify the attribute they get a warning telling them hands off, for which you ostensibly have a good reason:
>>> mc = MyClass('foo', 'bar')
>>> mc.user = 'huh?'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 10, in user
AttributeError: do not attempt to modify user member
>>> del mc.user
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 13, in user
AttributeError: do not attempt to modify user member
Without our optional setter and deleter methods we get a more generic error message:
>>> mc.user = 'huh?'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> del mc.user
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't delete attribute