Classes in python, variable confusion - python

So I'm learning about classes. Why can't I use the third block of code to do the same thing that the second block of code apparently does? Why do I have to assign p1 to person()and not just use person() in the way I do in the third block of code?
#class
class person:
def asdf(self):
self.firstname=""
self.lastname=""
self.id=""
self.email=""
self.friends=[]
#second block of code
p1 = person()
p1.firstname="Dave"
p1.lastname="Johnson"
p1.id="2345239"
p1.email="dave#gmail.com"
print p1.firstname
#third block of code
person().firstname="Dave"
person().lastname="Johnson"
person().id="2345239"
person().email="dave#gmail.com"
print person().firstname

In the second block, you change the properties of the same instance.
p1 = person() # create new instance
p1.firstname="Dave" # change its first name
p1.lastname="Johnson" # change its last name
# ...
print p1.firstname # access its firstname
In the third block, you create a new instance in each line.
person().firstname="Dave" # create new instance and change its firstname
person().lastname="Johnson" # create new instance and change its lastname
# ...
print person().firstname # create new instance and access its firstname
To be more accurate, the problem only occurs in the last line, as you try to access an attirbute that was not yet declared, since the firstname attribute is declared only in the function asdf, or, in the second block, in the line p1.firstname="Dave"
Here's a simple example:
>>> class A:
... def AA(self):
... self.a = 1
...
>>> a = A()
>>> a.a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'a'
>>> a.a = 1
>>> a.a
1

In the third block of code, each call to person() creates a new instance of the person class. The code then sets the value of an attribute on that object, then discards the entire object because it is not stored in any variable.

FYI, the python style is to use camel case on classes. This is part of the problem. The initial cap helps distinguish class construction from method calls.
Defining a class:
class Person(object):
def asdf(self):
self.firstname=""
self.lastname=""
self.id=""
self.email=""
self.friends=[]
Making an instance:
p = Person()
Using the class name like a function call constructs an instance, using the class as a prototype. In the second block, you've created an instance and you're changing its attributes.
In the third block, you've created four new instances of person, set one attribute on them, and thrown them away (because they're not assigned to anything).
The writeup on classes is really worth a read:
http://docs.python.org/tutorial/classes.html
There's a fair amount of underlying implementation that you don't need, but there's also some critical details about classes, instances, and attributes.

Related

Basics about Python class

I'm trying to learn about the basic construction of the python class. I have the following code which is completely running fine:
class Test1:
name = 'Choton'
age = 28
class Test2: # __init__ function executes whenever the class is called
def __init__(self):
self.name = 'Choton'
self.age = 28
print('Class Test2 is called and initialized...')
def main():
# Test1
print(f'Test1:')
print(f'Test1().name = {Test1().name}, Test1().age = {Test1().age}')
print(f'Test1.name = {Test1.name}, Test1.age = {Test1.age}\n')
# Test2
print(f'Test2:')
print(f'Test2().name = {Test2().name}, Test2().age = {Test2().age}')
# DOES NOT WORK
# print(f' name = {Test2.name}, age = {Test2.age}')
if __name__ == '__main__':
main()
I know that the __init__ function always runs whenever a class is initialized. Here the Test1 class do not have any __init__ function and both Test1.name and Test1().name works fine. But whenever I am calling the name using the __init__ function in Test2, the Test2.name does not work anymore. It says the following error if I uncomment that line: AttributeError: type object 'Test2' has no attribute 'name'.
What is the difference in here? Why both syntax work for one class and not for the other? I am a bit confused about it. Is there any syntactic sugar in here or any generalization that I am missing?
In Test1 you are defining class variables, which are shared between all objects, and can be accessed by using the class only (Test1.name) as well as from the instance (Test1().name).
Defining self.name in Test2.__init__ makes it an instance variable, which is only available on a specific instance of the class, e.g. new object created by Test2()
Took me a long time to get my head around classes, I'm still not there. The init function isn't needed in every class, one thing it's used for is to allocate different values to the class attributes.
For info,
Test1 is the class.
Test1() is an instance of the class.
Both can be assigned. So you could say:
MyClassInstanceA = Test1()
MyClassInstanceB = Test1()
... These two are different items because they are both instances of the Test1 class, each instance is unique.
Or you could say
MyClassA = Test1
MyClassB = Test1
... These two are the same thing as they are both allocated Test1, not Test1()....
name and age are attributes. In the case of Test1 they are "class attributes". That is, the exist at the class level. They can be accessed via the class or an instance of the class. Remember Test1 is the class Test1() is an instance of the class.
The subtle difference between Test1() and Test1 is what is causing the error.
To further explain. In the Test1 class, the two attributes name and age exist in the class, before we create an instance of the class. If we do create any instances name and age attributes will be the same across all instances. But the important thing here is we don't have to create an instance of the class to access them. That's why Test1.name works....
On the other hand, in Test2 class. The attributes are only created when the class is initiated (this is what the init function is doing). They don't exist until then. So, when we want to access them you need to access them via an instance of the class Test2().name....
MyClassC = Test2
... Still no instance of the class so name or age doesn't exist
MyClassInstanceD = Test2()
... Now we have an instance of the class so name and age exists and it is accessed via that class which is called MyClassInstanceD...
MyClassInstanceD.name = 'Choton'
But as name and age only exist in the instance we can still not call
Test2.name
as this still doesn't exist and never will unless you modify the Test2 class...

Python .__class__.__name__ works for one class and doesn't for another

I'm sorry if I come across as incredibly stupid, I'm relatively new to python and I can not figure out what I am doing wrong here.
I have two classes, and I try to get the class names of both with
*class*.__class__.__name__
Now, as I said, this works for one class, but doesn't for another.
These are my classes:
class fb():
val = 300
mult = 2.0
shsym = pygame.image.load('img/dummy2.png')
value = 50
class a():
occ = 0
shsym = pygame.image.load('img/it/a/shsym.png')
plsym = pygame.image.load('img/it/a/plsym.png')
value = 100
hesyms = [pygame.image.load('img/a/hesym0',pygame.image.load('img/dummy.png'))]
coord = [(50, 300),(30, 435),(310, 350)]
The variables inside probably don't really matter but as I can't figure out the problem I just included the whole thing.
Then I define them
fob = fb()
ita = a()
Then I set a variable as one of the defined classes
itemselect = fob
And then, finally, I try to check the class' name and look if it begins with 'f' (to see if it is part of a group of items)
if not itemselect.__class__.__name__.startswith("f"):
And in that line I get the error message
Traceback (most recent call last):
File "D:\Programmieren\Cola Atsume\main.py", line 283, in <module>
if not itemselect.__class__.__name__.startswith("f"):
AttributeError: class fa has no attribute '__class__'
When itemselect is ita everything works just fine, but with fob it doesn't.
I know I could do this differently, and my whole class system isn't really conventional and all but I don't really want to change it if I don't have to.
Note: This is grindy Python 2 stuff. In Python 3, things are different because in Python 3, every class is "new style" by default. New-Style classes themselves are also "simply" instances of their meta-class (often this meta-class is type). This means that a class in Python 3 (and a Python 2 class inheriting from object) in fact has a __class__ attribute (which carries its meta-class). Do not worry about meta-classes now.
When the error happens, itemselect is a class, not an instance, which is why it doesn’t have a __class__ attribute.
You say that:
Then I set a variable as one of the defined classes
Exactly, and classes do not have a __class__ attribute. Only instances (also called objects) of classes have that attribute:
>>> class Foo:
... pass
...
>>> hasattr(Foo, "__class__")
False
>>> f = Foo()
>>> hasattr(f, "__class__")
True
>>> f.__class__ is Foo
True
You need to distinguish classes and objects strongly, because in most cases it is the wrong thing to do to mix those.
Also, you really, really, really should not be doing this. You can easily convert this to an issubclass-based check.
Create an empty class called f:
class f:
pass
Now base all your classes whose name starts with f on that class:
class fb(f):
val = 300
mult = 2.0
shsym = pygame.image.load('img/dummy2.png')
value = 50
And now, instead of doing the nasty .startswith check, use issubclass:
if issubclass(itemselect, f):
That is already a lot cleaner than checking for the first character in the name of a class.
Also, as a beginner nowadays, you really really really should not be using Python 2 and now you don’t have an excuse to use old-style classes either, because you know new-style exists. It will make porting your code and your mental model of how Python works to Python 3 easier.

Instance variable in python

I am new in python i read about Instance variable:
A variable that is defined inside a method and belongs only to the
current instance of a class.
So i test it:
Consider this code:
class test:
count=0
def __init__(self):
test.count += 1
I have a class that add count after Instantiation.
I run this:
t1=test()
t1.count=100
and then I create new instance:
t2=test()
t2.count # 2
then create another instance:
t3=test()
t3.count # 3
t2.count # 3
and then :
t1.count # I get 100
My question is why t2 and t3 was update but if change value of instance variable of specific instance the instance 's instance variable was not update?
In t2 and t3, since you haven't defined an instance variable count, referring to, e.g., t2.count looks up the name in the class scope and evaluates to the class variable test.count.
In t1, you've created a completely different instance variable count that just happens to have the same name as the class variable. t1.count therefore returns this instance variable.
Because instance attributes shadow class attributes, but they are independent.
When you try to access t2.count, there is no instance attribute (t2.__dict__ does not have a count key), so Python next looks at type(t2) to see if it can find the attribute there. The class does have such an attribute, so test.count is returned.
t1.count on the other hand finds the instance attribute and returns that. test.count is never considered.

How to avoid object creation in python?

I am new to python programming,I have one class,for this class i created one object( obj1).i don't want to create other than this object,if any body wants to create one more object for this class that should refer to first object only(instead of creating one more object).how to do this? please refer the below code?
class MyClass:
def __init__(self):
pass
obj1=MyClass()//create object
obj2=MyClass()//avoid creation and refer obj2 to obj1
obj3=MyClass()//avoid creation and refer obj3 to obj1
So you want something singleton-ish? Then do not use objects for this at all. Simply put the functions in a separate module (.py file) and put your variables in the module scope (e.g. global variables) - that's the pythonic way to do what you want if you do not need thread safety. Remember: It's not java and using classes for everything is not the way to go.
However, here's some code that allows only one instance:
class MyClass:
def __init__(self):
if getattr(self.__class__, '_has_instance', False):
raise RuntimeError('Cannot create another instance')
self.__class__._has_instance = True
If you want singletons, have a look at Python and the Singleton Pattern and Is there a simple, elegant way to define singletons?
Here's a simple way -- hide the class name:
class obj:
pass
obj = obj()
Which will make class obj instances more difficult to create afterwards -- but not impossible, as pointed out in the comments.
Another alternative, delete the class after its first use:
class MyClass:
def method(self): print 'spam'
obj1 = MyClass()
del MyClass
obj1.method() # show instance still exists
obj2 = MyClass()
Output:
spam
Traceback (most recent call last):
File "noclass.py", line 7, in <module>
obj2 = MyClass()
NameError: name 'MyClass' is not defined
You could create the single object and make it a global i.e top-level object in the module using it if all you are coding would go in a single file or you could put it in a seperate module and import it.

Function for class exemplar

I have something like that in my python code
class A:
__mess = "Yeap!"
def doSome(self):
self.FN()
def FN(self):
pass
def myFN(self):
print self.__mess
b = A()
b.FN = myFN
b.doSome()
But this doesn't work. Where am I wrong?
python 2.6.5
upd: I want to redefine method (FN) only for one exemplar (b).
upd2:
import new
class A:
__mess = "Yeap!"
def doSome(self):
self.FN()
def FN(self):
pass
def myFN(self):
print self.__mess
b = A()
b.FN = new.instancemethod(myFN, b, A)
b.doSome()
Doesn't work too.
Traceback (most recent call last):
File "", line 1, in
File "", line 4, in doSome
File "", line 2, in myFN
AttributeError: A instance has no
attribute '__mess'
myLoopFN is a function, not an instance method. Do
import new
b.loopFN = new.instancemethod( myLoopFN, b, A )
The problem is that Python treats instance methods very slightly differently to regular functions: they get the instance upon which they are run as the default first argument. If you define a method inside a class definition it automagically becomes an instance method, so that when you instantiate the class it gets passed the instance. However, when you define myLoopFN you do it outside the class definition, so that it is an ordinary function instead of an instance method. You fix this by explicitly declaring it as an instance method.
...
BUT
This is icky because it's not something you should do; changing instance methods at runtime will lead to problems. You'll never be sure whether your A is an original A or a modified one, and you won't be able to debug it because you can't tell whether you've changed loopFN or not. This will give you the kind of bugs that Nyarlathotep himself would be proud of.
The right way to do this is to subclass A and override the method, so that you can distinguish between the different classes.
class myA( A ):
def loopFN(self):
#put modified function here
This way, you can instantiate the modified class and be certain of its methods.
Edit
You are using a double-underscore variable name, __mess. You (almost certainly) don't want to do this. For some reason known only to our Benevolent Dictator for Life and a select few others, Python automatically mangles these __ names to _<class-name>__, to serve as a sort-of faux private variable. This is horrible, and besides there's no reason to call it __mess instead of (the much nicer) mess.
If you absolutely must call it __mess, you can refer to it as follows:
def myFN(self):
print( self._A__mess )
(mutatis mutandis when you change the name of A). This is nasty and unPythonic.
Regarding the second error (with __mess):
Change
print self.__mess
to
print self._mess
And change
class A:
__mess = "Yeap!"
to
class A:
_mess = "Yeap!"
Double underscores tell Python to use name-mangling.
An alternative is to change
def myFN(self):
print self.__mess
to
def myFN(self):
print self._A__mess

Categories

Resources