class Applicant:
applicant_id_count=1000
application_dict={
"A":0,
"B":0,
"C":0
}
def __init__(self,applicant_name):
self.__applicant_name=applicant_name
self.__applicant_id=None
self.__job_band=None
I need to make the static variables in the above class i.e. application_dict and applicant_id_count as private static variables. Or is there any such thing in python?
Python does not have access modifiers. If you want to access an instance (or class) variable from outside the instance or class, you are always allowed to do so.
That said there's a convention using underscores(_) that most developers follow to indicate that a variable/method is private. A single underscore is a convention of saying that it's a private variable but it actually doesn't change the access privilege. Example:
class Applicant:
_applicant_id_count = 1000
Applicant._applicant_id_count # equals to 1000
If you want to emulate private variables for some reason, you can always use the __ prefix. Python mangles the names of variables so that they're not easily visible. Example:
class Applicant:
__applicant_id_count=1000
You will get the following error when someone tries to directly access it:
Applicant.__applicant_id_count
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: class Applicant has no attribute '__applicant_id_count'
Someone can hack their way and use the variable like this:
Applicant._Applicant__applicant_id_count # prints out 1000
You can read more about it here: https://www.geeksforgeeks.org/private-variables-python/
In Python, you can always access all variables. But, there is a convention for naming of this classes and attributes. You can use the __ prefix (two underscores) from PEP 8. Python mangles the names of variables like __foo so that they're not easily visible to code outside the class that contains them. Also, if you want a protected variable scope, you can use _ prefix (one underscore).
Related
I'm trying to generate the values that will go into a custom enum instead of using literals:
from enum import IntEnum
class Test(IntEnum):
for i in range(3):
locals()['ABC'[i]] = i
del i
My desired output is three attributes, named A, B, C, with values 0, 1, 2, respectively. This is based on two expectations that I've come to take for granted about python:
The class body will run in an isolated namespace before anything else
locals during that run will refer to said isolated namespace
Once the body is done executing, I would expect the result to be not much different than calling IntEnum('Test', [('A', 0), ('B', 1), ('C', 2)]) (which works just fine BTW).
Instead, I get an error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in Test
File "/usr/lib/python3.8/enum.py", line 95, in __setitem__
raise TypeError('Attempted to reuse key: %r' % key)
TypeError: Attempted to reuse key: 'i'
If I try doing the same with class Test: instead of class Test(IntEnum):, it works as expected. The traceback is showing the problem to be happening in enum.py. This contradicts my assumptions about how things work.
What is going on with this code, and how to I create attributes in the local namespace of the class body before IntEnum can get to them?
Background The reason that I'm trying to create the enum this way is that the "real" values are a more complex tuple, and there is a __new__ method defined to parse the tuple and assign some attributes to the individual enum objects. All that does not seem to be relevant to figuring out what is happening with the error and fixing it.
First, an explanation of what is happening. Before executing the class body, the metaclass's __prepare__ method is used to create the namespace. Normally, this is just a dict. However, enum.EnumType uses a enum._EnumDict class, which specifically prevents duplicate names from being added to the namespace. While this does not alter how the code in the class body is run, it does alter the namespace into which that code places names.
There are a couple of exceptions to the duplicate prevention, which offer potential solutions. First, the proper solution is to use the _ignore_ sunder attribute. If it gets set first, the variable i can be used normally, and will not appear in the final class:
class Test(IntEnum):
_ignore_ = ['i']
for i in range(3):
locals()['ABC'[i]] = i
Another, much hackier method is to use a dunder name, which will be ignored by the metaclass:
class Test(IntEnum):
for __i__ in range(3):
locals()['ABC'[__i__]] = __i__
del __i__
While this solution is functional, it uses dunders, which are nominally reserved by the language, and an undocumented feature, both of which are bad.
If I have defined a function I expect to use in most of my programs, where do I store it so that I can just import it without having to have it in the same folder as my program?
Also when should I call such functions as opposed to importing them? (I am not sure what the correct terminology is) i.e.
When would you set up a function so that you use it by:
myFunction()
And when would you set it up so that you use by :
import myFunction as mf
...
mf.blahblah
Please try below
Access modifier for your function : public
Now you can create instance of the class. And you can easily use same function from everywhere.
Today I came across the following pylint error:
invalid-all-object (E0604):
Invalid object %r in __all__, must contain only strings Used when an invalid (non-string) object occurs in __all__.
And I'm quite curious to why is it considered incorrect to expose objects directly?
Because it's supposed to be a list of names, not values:
If the list of identifiers is replaced by a star ('*'), all public names defined in the module are bound in the local namespace for the scope where the import statement occurs.
The public names defined by a module are determined by checking the module’s namespace for a variable named __all__; if defined, it must be a sequence of strings which are names defined or imported by that module. The names given in __all__ are all considered public and are required to exist. If __all__ is not defined, the set of public names includes all names found in the module’s namespace which do not begin with an underscore character ('_'). __all__ should contain the entire public API. It is intended to avoid accidentally exporting items that are not part of the API (such as library modules which were imported and used within the module). [Language Reference]
If you expose something other than a string, Python will throw an exception. This is why pylint gives that error, because the code is incorrect.
File mymodule.py:
def func():
pass
__all__ = [func]
Now run:
from mymodule import *
You will get a TypeError.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: attribute name must be string, not 'function'
The reason is that __all__ is used to name attributes on the module object. That's just how the mechanism works. If you wanted to modify Python's import mechanism so that you could just put objects there, I suppose you could, but it would only work with certain types of objects (functions and classes would work, but constants would not work, and you wouldn't be able to rename functions and classes).
How to explicitly declare a list as a local variable that cannot be touched from anywhere except the function its declared in?
tried
LOCAL variable = []
doesnt work
Python is the interpreted language and it hasn't such statements.
Here is from the documentation:
“Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail and subject to change without notice
You can notice such _pseudo_private_var(one underscore) naming.It is just a convention, it just tells you SHOUDN'T touch this variable but you CAN change value of this one at the same time. It is a python). There is __dynamic_obj_relative_naming as well(two underscores).
More about scopes and namespaces rules you could find here Classes, namespace and scope rules. There are few interesting statements you are interested in probably global and nonlocal. They are like opposites of yours.
I have seen code in which functions/constants are prefixed with underscores.
My understanding is that this indicates that they are not to be used directly.
Can I do this with classes ?
class _Foo(object):
pass
class __Bar(object):
pass
Better only use one _. This indicates that a name is private within a module.
It is not imported with the catch-all from <module> import *, and it has some other features such as "preferred destruction".
From here:
If __all__ is not defined, the set of public names includes all names
found in the module’s namespace which do not begin with an underscore
character ('_').
From here:
Starting with version 1.5, Python guarantees that globals whose name begins with a
single underscore are deleted from their module before other globals are deleted.
Double-underscore starting class members are name-mangled.
Yes; the single underscore usage is endorsed by PEP8 for internal-use classes.
I don't believe the double underscore usage will have any real effect most of the time, since it's used to active name mangling for class attributes, and generally a class isn't an attribute of another class (granted, it can be, in which case Python will happily mangle the name for you.)
Yes, and this is not only a convention. When you import * from this module, names starting with underscore will not be imported.
You can use a single underscore as the first character in any variable, but it is carries the implied meaning, "Do not use outside of the class/module unless you really know what you're doing" (eg. intended protected/internal) and it will not import if you use from <module> import *.
Using a double underscore is something you should never do outside of a class as it could mess with name mangling otherwise (and by "could", I mean, "caused me a big headache this past week because I did not realize that it does").