Difference between X.func and X.func( ) - python

I've come across many such situations where I have used in built functions or modules where the syntax is sometimes "X.func_name" and other times (X.func_name()".
For example :
In Pandas "df.columns" gives names of all columns and throws and throws error if written by mistake as "df.columns()" #TypeError: 'Index' object is not callable.
Also in Pandas, "count()", "min()" etc are written as df.count() | df.min()
I hope I have explained my question properly.
I believe it has something to do with the OOP concept of Class and it's member functions but I'd like a more in-dept understanding.

The syntax to access an attribute foo of an object or module is always .foo.
An attribute may or may not be callable. An int is not callable (it's just a number), but a function is callable. To call a function foo, you use parentheses, possibly with parameters inside, e.g. foo() or foo("bar"). Attempting to call something that is not callable will give you a TypeError.
So what syntax you use depends on whether the attribute is itself the value you want (e.g. an int or a str, or if it's a function that will return that value). In your example, columns is itself an int, whereas count is a function that you need to call in order to get an int.
Note that it's possible in Python to wrap any value in a function, or to turn a function into a property (i.e. make an attribute that automatically calls a function to produce its value), but in general the convention is that if something requires some kind of dynamic computation it will be a function, and values that are predetermined will not require a function invocation to retrieve.

The functions with parens are functions (actually class methods), which can take parameters and so on. Without parentheses, these are class variables.

Related

How come some methods don't require parentheses?

Consider the following code:
num = 1 + 1j
print(num.imag)
As opposed to
word = "hey"
print(word.islower())
One requires parentheses, and the other doesn't. I know in Python when we call functions without parentheses, we get back only a reference to the function, but it doesn't really answer it. So 'imag' returns a reference? because it seems the method does get executed and returns the imag part.
imag is not a method. It's simply a number-valued attribute.
islower is a method. In order to call the method, you put parentheses after the name.
num.imag is not a function, it's an attribute. To call a function you need the parentheses, or the __call__ method.
Attributes (e.g. imag) are like variables inside the object so you don't use parentheses to access them. Methods (e.g. islower()) are like functions inside the object so they do require parentheses to accept zero or more parameters and perform some work.
Objects can also have 'properties' that are special functions that behave like attributes (i.e. no parentheses) but can perform calculation or additional work when they are referenced or assigned.

Approach behind having everything as an object in Python

Why is everything in Python, an object? According to what I read, everything including functions is an object. It's not the same in other languages. So what prompted this shift of approach, to treat everything including, even functions, as objects.
The power of everything being an object is that you can define behavior for each object. For example a function being an object gives you an easy way to access the docs of the function for introspection.
print( function.__doc__ )
The alternative would be to provide a library of function that took
a function and returned its interesting properties.
import function_lib
print( function_lib.get_doc( function )
Making int, str etc classes means that you can extend those provide types
in interesting ways for your problem domain.
In my opinion, the 'Everything is object' is great in Python. In this language, you don't react to what are the objects you have to handle, but how they can interact. A function is just an object that you can __call__, a list is just an object that you can __iter__. But why should we divide data in non overlapping groups. An object can behave like a function when we call it, but also like an array when we access it.
This means that you don't think your "function" like, "i want an array of integers and i return the sum of it" but more "i will try to iterate over the thing that someone gave me and try to add them together, if something goes wrong, i will tell it to the caller by raiseing error and he will hate to modify his behavior".
The most interesting exemple is __add__. When you try something like Object1 + Object2, Python will ask (nicely ^^) to Object1 to try to add himself with object2 (Object1.__add__(Object2)). There is 2 scenarios here: either Oject1 knows how to add himself to Object2 and everything is fine, either he raises a NotImplemented error and Python will ask to Object2 to radd himself to Object1. Just with this mechanism, you can teach to your object to add themselves with any other object, you can manage commutativity,...
why is everything in Python, an object?
Python (unlike other languages) is a truly Object Orient language (aka OOP)
when everything is an object, it becomes easier to search, manipulate or access things. (But everything comes at the cost of speed)
what prompted this shift of approach, to treat everything including, even functions, as objects?
"Necessity is the mother of invention"

Is this an example of python function overload?

I know python does not allow us to overload functions. However, does it have inbuilt overloaded methods?
Consider this:
setattr(object_name,'variable', 'value')
setattr(class_name,'method','function')
The first statement dynamically adds variables to objects during run time, but the second one attaches outside functions to classes at run time.
The same function does different things based on its arguments. Is this function overload?
The function setattr(foo, 'bar', baz) is always the same as foo.bar = baz, regardless of the type of foo. There is no overloading here.
In Python 3, limited overloading is possible with functools.singledispatch, but setattr is not implemented with that.
A far more interesting example, in my opinion, is type(). type() does two entirely different things depending on how you call it:
If called with a single argument, it returns the type of that argument.
If called with three arguments (of the correct types), it dynamically creates a new class.
Nevertheless, type() is not overloaded. Why not? Because it is implemented as one function that counts how many arguments it got and then decides what to do. In pure Python, this is done with the variadic *args syntax, but type() is implemented in C, so it looks rather different. It's doing the same thing, though.
Python, in some sense, doesn't need a function overloading capability when other languages do. Consider the following example in C:
int add(int x, int y) {
return x + y;
}
If you wish to extend the notion to include stuff that are not integers you would need to make another function:
float add(float x, float y) {
return x + y;
}
In Python, all you need is:
def add(x, y):
return x + y
It works fine for both, and it isn't considered function overloading. You can also handle different cases of variable types using methods like isinstance. The major issue, as pointed out by this question, is the number of types. But in your case you pass the same number of types, and even so, there are ways around this without function overloading.
overloading methods is tricky in python. However, there could be usage of passing the dict, list or primitive variables.
I have tried something for my use cases, this could help here to understand people to overload the methods.
Let's take the example:
a class overload method with call the methods from different class.
def add_bullet(sprite=None, start=None, headto=None, spead=None, acceleration=None):
pass the arguments from remote class:
add_bullet(sprite = 'test', start=Yes,headto={'lat':10.6666,'long':10.6666},accelaration=10.6}
OR add_bullet(sprite = 'test', start=Yes,headto={'lat':10.6666,'long':10.6666},speed=['10','20,'30']}
So, handling is being achieved for list, Dictionary or primitive variables from method overloading.
try it out for your codes

Trying to cast one object type into another in Python

I have this bit of code:
const ON_Curve* curve = ...;
const ON_NurbsCurve* nurb = ON_NurbsCurve::Cast( curve );
if( nurb )
{
ON_Ellipse ellipse;
double tolerance = model.m_settings.m_ModelUnitsAndTolerances.m_absolute_tolerance;
bool rc = nurb->IsEllipse( 0, &ellipse, tolerance );
It casts a ON_NurbsCurve object to ON_Curve object. I am not quite sure if that's even possible in Python. I know i can take a string and cast it into an integer like: int("1"). I am not sure what is the right way to do so with other object types that are not built in.
thank you
You can't exactly cast objects in Python, nor do you generally need to because Python doesn't have strong type-checking.
Technically, casting is interpreting an existing object's data as if it were another type, essentially treating the object as a liquid metal that is being cast into a new shape (the origin of the term). In Python, what you can do is try to convert an object to another format. Usually an object that can take another object as input will accept it in its constructor (e.g. int() can take strings as well as numbers, and will call __int__() on other types if such a method exists, to let other types define how they are converted). Some types even have alternate constructors if their main constructor can't accept a given kind of object (for example, an XML parser might accept a filename in its main constructor, and have from_string() and from_file() class methods that accept strings and file-like objects, respectively).
In the example you give, of converting one type of Curve object into another, in Python you probably wouldn't even need to do any conversion. The NurbsCurve object probably supports the methods and attributes of Curve that any function that accepts a Curve would expect to see, even if it isn't a strict subclass. And if it is a strict subclass, then there's definitely no problem and no need to convert!
Python doesn't check argument types unless there is explicit code to do so, and most functions don't have such code. Instead, they just assume the caller is not a doofus and has passed in an object they can use. This is commonly called "duck typing."
If a given function doesn't accept the object you want to pass in, you could write a conversion function, a multiple-inheritance subclass, or a wrapper class to make what you have behave enough like the type that's needed to get the function to work. But this is usually not needed, because people who design APIs are not idiots and will be generous in what they accept when possible.

Returning a specific data type when referring to an object in Python

Usually when outputting an object in Python, you define the string that is returned in __repr__, but what if you want it to return something else instead, like an integer or tuple?
I'll give an example. My class is Users and there are member variables self.username and self.email. If I want to output an object of type Users, how do I get it to return (self.username,self.email) as a tuple?
You can't. The return value from __repr__ must be a string, and the content of that string should represent a valid Python expression which can be passed to eval() to create a new object of that type if possible.
https://docs.python.org/2/reference/datamodel.html#object.repr
The __*__ attributes of an object are meant to implement internal functions standardized by the Python language. Like __add__ (which is used to provide a result of object + whatever, __repr__ is expected to behave in a defined way, which would include to return (a) certain datatype(s).
While statically typed languages will report a compile-time error, for dynamically typed languages like Python, this might result in unexpected (yet not undefined!) runtime behaviour. This need not even result in an error message. Therefore, never change that behaviour to somethin unexpected.
If you want to return something custom, use a custom method like get_info(self) or similar. (Remember not to use __*__ names for that either)

Categories