I haven't found anything. I want to do this without passing the name inside the constructor.
class foo:
pass
something = foo()
I want each instance of foo to be aware of its initial identifier name. In this case the instance of foo() I created should be aware that its identifier is the string "something"
I am working on a small api and this would be a very elegant approach, unfortunately I haven't seen anything like this done.
Thanks,
Even if you could do this—which you can't—it would be an abuse of the language. For the sanity of whoever reads your code, please avoid hidden "magic" behavior. Python programmers would not expect their local variable names to be meaningful and actually be exposed to outside code.
You can't. Creation of an object is totally independent from assigning a reference to that object to some named variable.
foo().doSomething() — an instance of foo is not bound to any name and probably gets garbage collected right after the call.
a[1].moo = foo() — what's the name, again?
a = foo()
b = a # is name of foo 'a' or 'b'? both point to the same instance
a = None # but 'a' is gone, so now the name is 'b'?..
OTOH, passing a name to the constructor is painless:
my_foos = {}
for name in ('a', 'b', 'c'):
my_foos[name] = foo(name)
You may even rudely assign an instance attribute, if you won't change the constructor for some reason:
my_foos = {}
for name in ('a', 'b', 'c'):
a_foo = foo()
a_foo.my_mame = name # here
my_foos[name] = a_foo
And if you're into the dark side, you finally can add your foos to global namespace:
globals().update(my_foos) — now you have global names a, b and c, each referring to an aptly-named foo.
And, for foo's sake, name your classes with a capital initial letter :)
As far as I know, in Python a variable is just a name referring to some object instance. You can have many names pointing to the same instance; the instance is not aware of its names (nor should be, IMHO); the same name can be reused later to point to other instance from a complete different class.
Given the Python data model is hard to see why would be useful for the object to know how it is named in some namespace.
You can store references to your instances in a class variable or another container, but it is not very common in Python - because namespaces are such natural and elegant containers.
Related
I just started programming in Python, and there's something OOP-related that I'm not quite clear on. So in Python, you can create and assign value to a new variable without needing to first declare it. So with that, is there any difference between creating and assigning a new variable for self (eg. self.variable = 5) inside a function (eg. __init__()) vs. creating and assigning a new private member variable? It seems to me like there is no difference at all.
class Foo:
__var_A = 5;
def __init__(self):
self.__var_B = 5;
self.printVars()
def printVars(self):
print(self.__var_A)
print(self.__var_B)
foo = Foo()
There is in fact a difference between those two variables:
Since __var_A is defined in class Foo instead of an individual member-function like __init__, you can change it for all instances at once like this:
Foo._Foo__var_A = 2
This will not work for __var_B since you define it individually per instance.
Note though, that changing __var_A on an instance will not change it for everyone else, it will instead put a local override into the object itself:
foo2 = Foo()
foo2._Foo__var_A = 1
Foo._Foo__var_A = 2
(foo2._Foo__var_A, foo._Foo__var_A) # Returns: (1, 2)
It seems to me like there is no difference at all.
That is correct.
But remember that Python does not have 'private' members. That is only a convention.
The difference between protected and public is a matter of convention. And class or member variable prefixed by one _ indicates to a developer "don't use this unless you know what you're doing." Private is a SLIGHTLY different case, however: they require two _ and they cannot be suffixed by more than one _. Here are the docs:
“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.
Name mangling is an important part of Python inheritence. It allows classes to protect API calls from accidental manipulation of descendants (see above docs). However, if necessary you can still access them via _<class-name><var name>. Eg:
class Foo:
def __init__(self):
self.__bar = 1
print(Foo()._Foo__bar)
# 1
There is no difference if the variable is declared under class foo: of within the __init__(self). Both methods accomplish the same thing.
However, this is slight a twist if the variable (e.g. self.bar) is declared from a method other than __init__. Using
def bar(self):
self.bar = 'test'
creates a variable within the object that is not part of the default class. You can do this, but it is not good programming practice for python.
There is a difference. Consider following example:
foo = Foo()
foo.__dict__
This will return:
{'_Foo__var_B': 5}
But, following code:
bar = Foo
bar.__dict__
will return
{'_Foo__var_A': 5,
'__doc__': None,
'__init__': <function __main__.__init__>,
'__module__': '__main__',
'printVars': <function __main__.printVars>}
This leads to conclusion that __var_A will be accessible even if Foo is not instantiated, while __var_B will not be.
This question already has answers here:
How to convert variable into string in python
(9 answers)
Getting an instance name inside class __init__() [duplicate]
(10 answers)
Closed 6 years ago.
I know this is a weird idea. The idea would be something like this:
class AnyClass:
def __init__(self):
# print object name
Then create a new object
test = AnyClass()
And finally get this output:
'test'
That is not the idea behind this, but is an easy example of what I'm trying to...
PS: I'm not trying to get the class name, just the object name (if possible)
PS2: I know I can get the name with test.__name__ but I'm trying to get the name inside the class, not outside.
Consider this:
>>> a = dict()
>>> b = a
Both a and b reference the exact same object.
>>> a is b
True
When you do a . operation on an object, you're looking up an attribute on that object. An object can be referenced in many different locations; it makes no sense for it to store all those reference names, especially when those names are only bound within certain contexts. For example
def generator():
a = dict()
yield a
b = next(generator())
Both a and b refer to the same dict object, but you can't use a to reference the dict anywhere else besides in the generator function.
Within a specific context, you can test the bound names and see if they refer to a specific object.
test = MyObject()
for name, obj in locals().items():
if test is obj:
print name
First: you don't want to do this, there is no reason to do this, and if you think you need to do this, you're wrong.
Second: you can't do it in the __init__ method because the name reference test referring to the new AnyClass instance object hasn't been added to the memory space ("bound") yet. However, you could do it like this.
class AnyClass():
def echo_name(self):
{v:k for k,v in locals().items()}[self]
test = AnyClass()
test.echo_name()
This will return the first variable encountered in the locals() dictionary that is assigned to the test object. There is no guarantee for the order in which those variables will be returned.
To explain a bit further about why it won't work in the __init__ method, when you do this:
test = AnyClass()
A new instance of AnyClassis constructed according to the instructions of the class definition (including the definitions of any parent or metaclass). This construction happens in phases, the last phase of which is executing the __init__ method. Prior to __init__, other methods that will be executed, if they exist, are __new__, and also the the __new__, __init__, and __call__ methods of the metaclass (if one exists).
So at the point in time the code in the body of the __init__ method is being executed, the object is still being constructed. Therefore there is, as of yet, nothing in the locals() dictionary assigned to the name 'test'. There is only a member called 'self'. And, obviously, if you reverse-lookup the self object in the locals() dictionary looking for a registered name, the name you will get is the name 'self'. Which... isn't useful.
I'm trying to understand the relationship between the variable a Python class object is assigned to and the __name__ attribute for that class object. For example:
In [1]: class Foo(object):
...: pass
...:
In [2]: Foo.__name__ = 'Bar'
In [3]: Foo.__name__
Out[3]: 'Bar'
In [4]: Foo
Out[4]: __main__.Bar
In [5]: Bar
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-5-962d3beb4fd6> in <module>()
----> 1 Bar
NameError: name 'Bar' is not defined
So it seems like I have changed the __name__ attribute of the class but I can't refer to it by that name. I know this is a bit general but could someone explain the relationship between Foo and Foo.__name__?
It's simple. There is no relationship at all.
When you create a class a local variable is created with name you used, pointing at the class so you can use it.
The class also gets an attribute __name__ that contains the name of that variable, because that's handy in certain cases, like pickling.
You can set the local variable to something else, or change the __name__ variable, but then things like pickling won't work, so don't do that.
__name__ is mere self-identification, in oder to know what type an instance of it really is.
The other thing is the way it can be accessed with. That can vary if you re-assign it.
They both are assigned at the time you define the class.
It works the same way with functions: if you def them, they get assigned to the given name and they get the respective __name__ attribute.
OTOH, if you have a lambda function, it gets a __name__ attribute of <lambda>, because it doesn't know the name it gets assigned to.
Short version
class Foo(object): pass creates a class and assigns it to local name Foo.
Foo.__name__ = 'Bar' assigns a new value to attribute __name__. The enclosing scope is not affected.
Long version
The class statement creates a class and assigns to the name provided in the local scope. When creating a class Python tells the class the name it was created with by assigning it to the class's __name__ attribute.
Assigning to a class's attribute does not introduce a name into the local scope. Therefore any changes to attributes (such as __name__) do not affect the enclosing scope.
You need to keep in mind that in python a class is just an object like any other. It wouldn't make sense for an object to contain an attribute that was linked to a variable that refers to the object, because there could be any number of variable names referring to it. Any time you write an assignment (Bar = Foo) or pass the object to a function, you have a new reference. Naturally all objects must be independent of how they are referenced.
__name__ is simply a piece of information attached to the class object, which happens to be the same as the variable name it's initially assigned to.
I want to clarify how variables are declared in Python.
I have seen variable declaration as
class writer:
path = ""
sometimes, there is no explicit declaration but just initialization using __init__:
def __init__(self, name):
self.name = name
I understand the purpose of __init__, but is it advisable to declare variable in any other functions?
How can I create a variable to hold a custom type?
class writer:
path = "" # string value
customObj = ??
Okay, first things first.
There is no such thing as "variable declaration" or "variable initialization" in Python.
There is simply what we call "assignment", but should probably just call "naming".
Assignment means "this name on the left-hand side now refers to the result of evaluating the right-hand side, regardless of what it referred to before (if anything)".
foo = 'bar' # the name 'foo' is now a name for the string 'bar'
foo = 2 * 3 # the name 'foo' stops being a name for the string 'bar',
# and starts being a name for the integer 6, resulting from the multiplication
As such, Python's names (a better term than "variables", arguably) don't have associated types; the values do. You can re-apply the same name to anything regardless of its type, but the thing still has behaviour that's dependent upon its type. The name is simply a way to refer to the value (object). This answers your second question: You don't create variables to hold a custom type. You don't create variables to hold any particular type. You don't "create" variables at all. You give names to objects.
Second point: Python follows a very simple rule when it comes to classes, that is actually much more consistent than what languages like Java, C++ and C# do: everything declared inside the class block is part of the class. So, functions (def) written here are methods, i.e. part of the class object (not stored on a per-instance basis), just like in Java, C++ and C#; but other names here are also part of the class. Again, the names are just names, and they don't have associated types, and functions are objects too in Python. Thus:
class Example:
data = 42
def method(self): pass
Classes are objects too, in Python.
So now we have created an object named Example, which represents the class of all things that are Examples. This object has two user-supplied attributes (In C++, "members"; in C#, "fields or properties or methods"; in Java, "fields or methods"). One of them is named data, and it stores the integer value 42. The other is named method, and it stores a function object. (There are several more attributes that Python adds automatically.)
These attributes still aren't really part of the object, though. Fundamentally, an object is just a bundle of more names (the attribute names), until you get down to things that can't be divided up any more. Thus, values can be shared between different instances of a class, or even between objects of different classes, if you deliberately set that up.
Let's create an instance:
x = Example()
Now we have a separate object named x, which is an instance of Example. The data and method are not actually part of the object, but we can still look them up via x because of some magic that Python does behind the scenes. When we look up method, in particular, we will instead get a "bound method" (when we call it, x gets passed automatically as the self parameter, which cannot happen if we look up Example.method directly).
What happens when we try to use x.data?
When we examine it, it's looked up in the object first. If it's not found in the object, Python looks in the class.
However, when we assign to x.data, Python will create an attribute on the object. It will not replace the class' attribute.
This allows us to do object initialization. Python will automatically call the class' __init__ method on new instances when they are created, if present. In this method, we can simply assign to attributes to set initial values for that attribute on each object:
class Example:
name = "Ignored"
def __init__(self, name):
self.name = name
# rest as before
Now we must specify a name when we create an Example, and each instance has its own name. Python will ignore the class attribute Example.name whenever we look up the .name of an instance, because the instance's attribute will be found first.
One last caveat: modification (mutation) and assignment are different things!
In Python, strings are immutable. They cannot be modified. When you do:
a = 'hi '
b = a
a += 'mom'
You do not change the original 'hi ' string. That is impossible in Python. Instead, you create a new string 'hi mom', and cause a to stop being a name for 'hi ', and start being a name for 'hi mom' instead. We made b a name for 'hi ' as well, and after re-applying the a name, b is still a name for 'hi ', because 'hi ' still exists and has not been changed.
But lists can be changed:
a = [1, 2, 3]
b = a
a += [4]
Now b is [1, 2, 3, 4] as well, because we made b a name for the same thing that a named, and then we changed that thing. We did not create a new list for a to name, because Python simply treats += differently for lists.
This matters for objects because if you had a list as a class attribute, and used an instance to modify the list, then the change would be "seen" in all other instances. This is because (a) the data is actually part of the class object, and not any instance object; (b) because you were modifying the list and not doing a simple assignment, you did not create a new instance attribute hiding the class attribute.
This might be 6 years late, but in Python 3.5 and above, you can give a hint about a variable type like this:
variable_name: type_name
or this:
variable_name # type: shinyType
This hint has no effect in the core Python interpreter, but many tools will use it to aid the programmer in writing correct code.
So in your case(if you have a CustomObject class defined), you can do:
customObj: CustomObject
See this or that for more info.
There's no need to declare new variables in Python. If we're talking about variables in functions or modules, no declaration is needed. Just assign a value to a name where you need it: mymagic = "Magic". Variables in Python can hold values of any type, and you can't restrict that.
Your question specifically asks about classes, objects and instance variables though. The idiomatic way to create instance variables is in the __init__ method and nowhere else — while you could create new instance variables in other methods, or even in unrelated code, it's just a bad idea. It'll make your code hard to reason about or to maintain.
So for example:
class Thing(object):
def __init__(self, magic):
self.magic = magic
Easy. Now instances of this class have a magic attribute:
thingo = Thing("More magic")
# thingo.magic is now "More magic"
Creating variables in the namespace of the class itself leads to different behaviour altogether. It is functionally different, and you should only do it if you have a specific reason to. For example:
class Thing(object):
magic = "Magic"
def __init__(self):
pass
Now try:
thingo = Thing()
Thing.magic = 1
# thingo.magic is now 1
Or:
class Thing(object):
magic = ["More", "magic"]
def __init__(self):
pass
thing1 = Thing()
thing2 = Thing()
thing1.magic.append("here")
# thing1.magic AND thing2.magic is now ["More", "magic", "here"]
This is because the namespace of the class itself is different to the namespace of the objects created from it. I'll leave it to you to research that a bit more.
The take-home message is that idiomatic Python is to (a) initialise object attributes in your __init__ method, and (b) document the behaviour of your class as needed. You don't need to go to the trouble of full-blown Sphinx-level documentation for everything you ever write, but at least some comments about whatever details you or someone else might need to pick it up.
For scoping purpose, I use:
custom_object = None
Variables have scope, so yes it is appropriate to have variables that are specific to your function. You don't always have to be explicit about their definition; usually you can just use them. Only if you want to do something specific to the type of the variable, like append for a list, do you need to define them before you start using them. Typical example of this.
list = []
for i in stuff:
list.append(i)
By the way, this is not really a good way to setup the list. It would be better to say:
list = [i for i in stuff] # list comprehension
...but I digress.
Your other question.
The custom object should be a class itself.
class CustomObject(): # always capitalize the class name...this is not syntax, just style.
pass
customObj = CustomObject()
As of Python 3, you can explicitly declare variables by type.
For instance, to declare an integer one can do it as follows:
x: int = 3
or:
def f(x: int):
return x
see this question for more detailed info about it:
Explicitly declaring a variable type in Python
Wishing to avoid a situation like this:
>>> class Point:
x = 0
y = 0
>>> a = Point()
>>> a.X = 4 #whoops, typo creates new attribute capital x
I created the following object to be used as a superclass:
class StrictObject(object):
def __setattr__(self, item, value):
if item in dir(self):
object.__setattr__(self, item, value)
else:
raise AttributeError("Attribute " + item + " does not exist.")
While this seems to work, the python documentation says of dir():
Note: Because dir() is supplied primarily as a convenience for use at an interactive prompt, it tries to supply an interesting set of names more than it tries to supply a rigorously or consistently defined set of names, and its detailed behavior may change across releases. For example, metaclass attributes are not in the result list when the argument is a class.
Is there a better way to check if an object has an attribute?
Much better ways.
The most common way is "we're all consenting adults". That means, you don't do any checking, and you leave it up to the user. Any checking you do makes the code less flexible in it's use.
But if you really want to do this, there is __slots__ by default in Python 3.x, and for new-style classes in Python 2.x:
By default, instances of both old and new-style classes have a dictionary for attribute storage. This wastes space for objects having very few instance variables. The space consumption can become acute when creating large numbers of instances.
The default can be overridden by defining __slots__ in a new-style class definition. The __slots__ declaration takes a sequence of instance variables and reserves just enough space in each instance to hold a value for each variable. Space is saved because __dict__ is not created for each instance.
Without a __dict__ variable, instances cannot be assigned new variables not listed in the __slots__ definition. Attempts to assign to an unlisted variable name raises AttributeError. If dynamic assignment of new variables is desired, then add '__dict__' to the sequence of strings in the __slots__ declaration.
For example:
class Point(object):
__slots__ = ("x", "y")
point = Point()
point.x = 5 # OK
point.y = 1 # OK
point.X = 4 # AttributeError is raised
And finally, the proper way to check if an object has a certain attribute is not to use dir, but to use the built-in function hasattr(object, name).
I don't think it's a good idea to write code to prevent such errors. These "static" checks should be the job of your IDE. Pylint will warn you about assigning attributes outside of __init__ thus preventing typo errors. It also shows many other problems and potential problems and it can easily be used from PyDev.
In such situation you should look what the python standard library may offer you. Did you consider the namedtuple?
from collections import namedtuple
Point = namedtuple("Point", "x, y")
a = Point(1,3)
print a.x, a.y
Because Point is now immutable your problem just can't happen, but the draw-back is naturally you can't e.g. just add +1 to a, but have to create a complete new Instance.
x,y = a
b = Point(x+1,y)