Different ways of calling class functions - python

Say I have this code:
class ClassStyleA(object):
def FunctionA(self):
print "This is Style A."
class ClassStyleB(object):
def FunctionA(self):
print "This is Style B."
class InstanceStyleA(ClassStyleA):
def FunctionB(self):
print "This is also Style A."
InstanceStyleB = ClassStyleB()
If I want to call, for example, FunctionA, from InstanceStyleA, I need to type it like this:
InstanceStyleA().FunctionA()
If I want to call FunctionA from InstanceStyleB, I need to type it like this:
InstanceStyleB.FunctionA()
I need to add a set of parentheses after InstanceStyleA in order to call one of its functions, otherwise I get this error:
TypeError: unbound method FunctionA() must be called with InstanceStyleA instance as first argument (got nothing instead)
Similarly, if I try to add the parentheses after InstanceStyleB when calling one of it's functions, I get this error:
TypeError: 'ClassStyleB' object is not callable
Why is this the case? Why are they treated differently if they are both instances of a class?

In your example code, you actually have three classes and one object, all confusingly named:
ClassStyleA is a class, that inherits from object
ClassStyleB is also a class that inherits from object
InstanceStyleA is not an instance, it is also a class, that inherits from ClassStyleA.
InstanceStyleB is not a class, it's an instance of the class ClassStyleB
To create an instance from a class, you call the class. So, ClassStyleA(), ClassStyleB(), InstanceStyleA() are all instances. So is InstanceStyleB - it's a variable to which you assign the instance created with ClassStyleB.
Since InstanceStyleB (without parentheses) is already an instance (of ClassStyleB), and not a class, you can't call it. This explains your second TypeError.
Analogously, you can only call methods (functions defined in a class, in your example it's FunctionA and FunctionB) on an instance. This explains your first TypeError: InstanceStyleA (again, without parentheses), is a class.
(Pedantic aside: much of above is, technically speaking, utter lies, but for the sake of this discussion it's simpler to pretend Python has a clear separation between classes and instances, and functions and methods.)

When you call InstanceStyleA with parenthesis, you are creating an instance of InstanceStyleA... and then calling the member function on that instance. It is basically shorthand for:
obj = InstanceStyleA()
obj.FunctionA()
InstanceStyleB is not a class, it is set to an instance of ClassStyleB, so when you are referencing it, there is no need for parenthesis (or instantiating an object).
it would be the same as if you wrote:
ClassObject = ClassStyleB()
ClassObject.FunctionA()
The TypeError is because objects aren't callable. For instance:
d = {} #d is a dictionary
d() #d used as a function has no meaning
I hope this clears up the difference.

Related

Calling a method of a class before creating an object

Let's say we have a class, and we are calling it's method before creating an object:
class ParentClass:
def MyMethod(self):
self.a=5
print("I am Method")
ParentClass.MyMethod(ParentClass)
Why do we get a result?
Also, why hasattr(ParentClass,'a') is showing that instance variable is created?
You get a result because you ParentClass.MyMethod evaluates to a regular function that takes one argument, which you supplied. The function doesn't care what the type of self is, as long as you can define an attribute named a for it. The result of ParentClass.MyMethod(ParentClass) is to add an attribute named a to ParentClass.
ParentClass().MyMethod, on the other hand, produces an instance of method that wraps the function, due to the descriptor protocol. The method, when called, simply calls the function with the instance as the first argument and the rest of its own arguments. Because the function doesn't expect any more arguments, you would get a TypeError.

Are all methods are preceded by a dot(.) or just some of them. How do we define it needs a dot or not?

I was learning Python by using Python Crash Course and came upon this String and Method thing: It only said that the dot(.) after name in name.title() tells Python to make the title() method act on the variable name.
Not always, you can create a method dynamically:
from types import MethodType
def fn(x):
return x.var
class A:
def __init__(self):
self.var = 20
obj = A()
method_ = MethodType(fn, obj)
print(method_)
print(method_())
output :
<bound method fn of <__main__.A object at 0x000001C5E3F01FD0>>
20
A method is an instance of type MethodType and also it has an object bound to it, when method gets called, it's first parameter will always get filled with that object. Here fn() function's first parameter (x) will be filled with obj object.
The above answer is precise but i wanted to add to it.
Actually methods are functions that take objects as arguments and then return values based on that and as python is an Object Oriented Language therefore everything in python is an object.
When you call name.title():
then, python search for the title() method for the name object.And as all methods are designated to take the object as an argument:
`def title(self):
...+
`
This is what a method definition look like inside a class and the self argument here stands for the object calling the method.
And we do not have to specify it explicitly it is recognised by the python interpreter.
As in your case: name.title() the object calling the method title() is the name variable therefore here self is assigned the value of name that is the function call name.title() is equivalent to title(name) but the former is the correct syntax of calling the method whereas the latter one is for comprehesion purpose.
If you run title(name) it surely gonna raise an error.
But, as the title() method belongs to the str class you can always call str.title(name).
Hope i didn't confuse you instead of making it clearer...Happy coding..:)

Clarification on a paragraph in the Python Tutorial Documentation

I'm unclear as to what this one paragraph in the python tutorial documentation is saying.
(found here: https://docs.python.org/3/tutorial/classes.html#method-objects)
When an instance attribute is referenced that isn’t a data attribute, its class is searched. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object. When the method object is called with an argument list, a new argument list is constructed from the instance object and the argument list, and the function object is called with this new argument list.
From my current understanding, I think what it's saying is that whenever you reference an attribute of an instance of a class like in the 8th line of this little snippet here:
class MyClass():
attribute = "I am an attribute"
def func(self):
return "I am a function"
instance = MyClass()
print(instance.func())
When python sees
instance.func()
what it's really doing isn't looking for a method func "owned by" instance, it's looking for a function func owned by MyClass, then calling that function owned by MyClass with instance as the self parameter.
so basically it's the same thing as:
MyClass.func(instance)
I feel like I'm missing something subtle though. I don't understand what it means by
... a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object.
What is an abstract object?
What does it mean to "pack" a pointer?
What does it mean to "pack" multiple pointers?
Why even have a method object for instance if python is just going to look at MyClass's function object?
Why doesn't python just make methods be "owned by" their instances? Why even go through the whole process of calling MyClass's func instead of instance's func?
Why did the designers of the language decide to make it be this way?
"Abstract object" means that there isn't necessarily a real Python object being created, it's just a way of describing what's happening behind the scenes as if there were some object being created.
"packing" means that it's just collecting these things together into this abstract object.
So when you write
instance.func()
it internally creates something that represents the function combined with the instance. When this is called, the method function is called as you described, with the instance passed as the first argument (conventionally named self).
Why do this? So that you can pass these things around:
foo = instance.func
foo()
The value of foo contains that abstract object that represents the function combined with the instance.
Methods are owned by classes so that all instances of a class automatically get the same method. This is the essence of OO programming and the basis of inheritance among classes.

Python memory allocation, when using bound, static or class functions?

I am curious about this: what actually happens to the python objects once that you create a class that contains each one of these functions?
Looking at some example, I see that either the bound, static or class function is in fact creating a class object, which is the one that contains all 3 function.
Is this always true, no matter which function I call? and the parent object class (object in this case, but can be anything I think) is always called, since the constructor in my class is invoking it implicitly?
class myclass(object):
a=1
b=True
def myfunct(self, b)
return (self.a + b)
#staticmethod
def staticfunct(b):
print b
#classmethod
classfunct(cls, b):
cls.a=b
Since it was not clear: what is the lifecycle for this object class, when I use it as following?
from mymodule import myclass
class1 = myclass()
class1.staticfunct(4)
class1.classfunct(3)
class1.myfunct
In the case of static, myclass object get allocated, and then the function is run, but class and bound method are not generated?
In the case of class funciton, it is the same as above?
in the case of the bound function, everything in the class is allocated?
The class statement creates the class. That is an object which has all three functions, but the first (myfunct) is unbound and cannot be called without an instance object of this class.
The instances of this class (in case you create them) will have bound versions of this function and references to the static and the class functions.
So, both the class and the instances have all three functions.
None of these functions create a class object, though. That is done by the class statement. (To be precise: When the interpreter completes the class creation, i. e. the class does not yet exist when the functions inside it are created; mind boggling, but seldom necessary to know.)
If you do not override the __init__() function, it will be inherited and called for each created instance, yes.
Since it was not clear: what is the lifecycle for this object class,
when I use it as following?
from mymodule import myclass
This will create the class, and code for all functions. They will be classmethod, staticmethod, and method (which you can see by using type() on them)
class1 = myclass()
This will create an instance of the class, which has a dictionary and a lot of other stuff. It doesn't do anything to your methods though.
class1.staticfunct(4)
This calls your staticfunct.
class1.classfunct(3)
This calls you classfunct
class1.myfunct
This will create a new object that is a bound myfunct method of class1. It is often useful to bind this to a variable if you are going to be calling it over and over. But this bound method has normal lifetime.
Here is an example you might find illustrative:
>>> class foo(object):
... def bar(self):
... pass
...
>>> x = foo()
>>> x.bar is x.bar
False
Every time you access x.bar, it creates a new bound method object.
And another example showing class methods:
>>> class foo(object):
... #classmethod
... def bar():
... pass
...
>>> foo.bar
<bound method type.bar of <class '__main__.foo'>>
Your class myclass actually has four methods that are important: the three you explicitly coded and the constructor, __init__ which is inherited from object. Only the constructor creates a new instance. So in your code one instance is created, which you have named class1 (a poor choice of name).
myfunctcreates a new integer by adding class1.a to 4. The lifecycle of class1 is not affected, nor are variables class1.a, class1.b, myclass.a or myclass.b.
staticfunct just prints something, and the attributes of myclass and class1 are irrelevant.
classfunct modifies the variable myclass.a. It has no effect on the lifecycle or state of class1.
The variable myclass.b is never used or accessed at all; the variables named b in the individual functions refer to the values passed in the function's arguments.
Additional info added based on the OP's comments:
Except for the basic data types (int, chars, floats, etc) everything in Python is an object. That includes the class itself (a class object), every method (a method object) and every instance you create. Once created each object remains alive until every reference to it disappears; then it is garbage-collected.
So in your example, when the interpreter reaches the end of the class statement body an object named "myclass" exists, and additional objects exist for each of its members (myclass.a, myclass.b, myclass.myfunct, myclass.staticfunct etc.) There is also some overhead for each object; most objects have a member named __dict__ and a few others. When you instantiate an instance of myclass, named "class1", another new object is created. But there are no new method objects created, and no instance variables since you don't have any of those. class1.a is a pseudonym for myclass.a and similarly for the methods.
If you want to get rid of an object, i.e., have it garbage-collected, you need to eliminate all references to it. In the case of global variables you can use the "del" statement for this purpose:
A = myclass()
del A
Will create a new instance and immediately delete it, releasing its resources for garbage collection. Of course you then cannot subsequently use the object, for example print(A) will now give you an exception.

python and using 'self' in methods

From what I read/understand, the 'self' parameter is similiar to 'this'.
Is that true?
If its optional, what would you do if self wasnt' passed into the method?
Yes, it's used in similar ways. Note that it's a positional parameter and you can call it what you want; however there is a strong convention to call it self (not this or anything else). Some positional parameter must be there for a usable instance method; it is not optional.
The joy of Python
That is true to some extend. Methods are bound to the object instance they are a part of. When you see
def some_func(self, foo, bar)
The passing of self is sometimes implicit when you call, for example:
obj.some_func(foo_val, bar_val)
Which is equal (presuming obj is of class MyClass) to
MyClass.some_func(obj, foo_val, bar_val)
Because the method is bound to obj, the self argument gets populated. This is part of Python being explicit with what it means. In other languages, this just pops into scope, with Python there is some exposure of how this happens.
You can also pass methods around, and manually pass them self when not calling from a bound context.
The Python docs do a good Job:
xf = x.f
while True:
print xf()
will continue to print hello world until the end of time.
What exactly happens when a method is called? You may have noticed that x.f() was called >without an argument above, even though the function definition for f() specified an >argument. What happened to the argument? Surely Python raises an exception when a function >that requires an argument is called without any — even if the argument isn’t actually >used...
Actually, you may have guessed the answer: the special thing about methods is that the >object is passed as the first argument of the function. In our example, the call x.f() is >exactly equivalent to MyClass.f(x). In general, calling a method with a list of n arguments >is equivalent to calling the corresponding function with an argument list that is created >by inserting the method’s object before the first argument.
self is this, just you have to explicitly pass it and explicitly use it to refer to class methods/properties.
It isn't optional in class methods. You will get a TypeError if you try to define a classmethod without at least one argument (i.e., the self parameter).
However, you can call it something other than self, but I have never seen otherwise.
self refers to the object on which the method was called, much like this in C++. But it is important that self is merely a convention, you can name it as you like and pass instances of subclasses.
In classes a self variable (or cls for classmethods) is required. What you want to call it is your decision though. If you prefer you could call it this instead.
A classmethod is a method that gets the class as a first argument instead of a instance. It can be called without passing an instance.
i.e. with a classmethod you can do:
SomeObject.some_class_method()
while a normal method would require you to do
SomeObject().some_normal_method()
or
SomeObject.some_normal_method(instance)
self is definitely similar to this, however, in Python, the name self is just a convention, and could be named anything else. The variable is named after whatever you call it in the function's prototype (def function(whatever, params...):).
For instance methods, self IS actually required. For class or static methods, you need to specify that they should be treated as such, and then self is not required. For example:
def type_to_display(type):
"""Converts a pass type to the full written pass type."""
return list((pair[1] for pair in Pass.TYPE_CHOICES if pair[0] ==
type[0:1].upper()))[0]
type_to_display = staticmethod(type_to_display)
You will never be able to use an instance method in such a way that self is not passed in. For example, if I have an instance my_car of a Car class, and I use the Car class's drive instance method, the my_car instance will be implicitly passed into the drive method as the first parameter (self).
class Car:
def drive(self):
self.do_some_stuff()
my_car = Car()
my_car.drive() # actually calls Car.drive(my_car)

Categories

Resources