Ibrahim Quraish
Ibrahim Quraish

Reputation: 4095

Python obscure class syntax

I have a class definition in a python module like this (util/systemlinux.py)

class SysUtil():
  def __init__(self):
    pass

  def validateIP(self, ip):
    result = True
    try:
      if len(ip.split('.')) == 4:
         socket.inet_aton(ip)
      else:
         result = False
    except socket.error:
      result = False
    return result

And at the client side driver code (ClientSetup.py)

from util.systemlinux import SysUtil
class ClientSetup():
  def __init__(self):
    self._ip = SysUtil() # what is this doing?

  def checkIP(self):
    ret = SysUtil.validateIP(self._ip, '127.0.0.1')

Above is a good working code. My question is, the member variable _ip I thought is created as an instance of SysUtil class but I do not understand why it is being passed as a parameter to SysUtil.validateIP() function? More over this validateIP() accepts exactly 2 arguments out of that self is the first parameter which is implicitly passed. But here the code is not even complaining any error. Is this syntax allowed?

Upvotes: 0

Views: 197

Answers (2)

Giuppox
Giuppox

Reputation: 1621

You are right: _ip is an instance of SysUtil, however there is nothing special about that first self parameter.

The behaviour of _ip does not depend on it being defined as an attribute of a ClientSetup instance, so lets consider this example:

>>> class A:
...    def f(self):
...        return
...
>>> a = A()
>>> type(A.f)
function
>>> type(a.f)
method

A.f is still a function until A is instantiated! We call this bounding. Definition: a bound method in Python is a function that has an object associated with it. (The concept of unbound method has been deprecated in Python 3.0).

That's it. In the above example, there is no conceptual difference between A.f and a generic function in a "non-class scope"

def g(self):
    return

i.e. they can be called in the same way. g and A.f are just a function that accept an instance of A as first positional argument.

When you instantiate A into a, a.f is a method. In a method (bound to an instance) the first parameter self (may be named in another way) is implicitly passed as the instance itself. I.e. when calling a.f() self gets the value of a.

A simple function such A.f (or g) does not behave in such way, the first parameter is not implicit, since the function is not bound to any instance. However you can pass the instance explicitly, as you would do with a parameter in any normal function, i.e. A.f(a).

So... A.f(a) is the same as a.f().

In your case A is SysUtil. The line SysUtil.validateIP(self._ip, '127.0.0.1') could be replaced with self._ip.validateIP('127.0.0.1').

The above line-replacement is also recommended in terms of design style, as @RomanPerekhrest pointed out.

I suggest you to take a look at this docs.python.org article to a deeper insight at how Python classes work.

When would this be useful

This design choice would be useful when wanting to specify you want that very implementation to be executed. We may need this when the class is subclassed. In our example:

>>> class B(A):
...     def f(self):  # May depend on `A.f`.
...         print('From `B.f`')
...
>>> b = B()
>>> b.f()  # Would now print.
From `B.f`

Let's say we don't want to print anything, but still use an instance of B. We could do

>>> A.f(b)  # Won't print.

Upvotes: 0

Weijun Zhou
Weijun Zhou

Reputation: 4896

You are right about that the member variable _ip is an instance of SysUtil class. However, SysUtil.validateIP does not have any implicitly passed parameters because SysUtil is the name of a class, not an instance. SysUtil.validateIP(self._ip, '127.0.0.1') can be rewritten as self._ip.validateIP('127.0.0.1') provided that no ambiguity occurs. The way it is written here is simply saying, "I want to invoke the validateIP method, and I want to tell you explicitly that you should invoke the version implemented in the SysUtil class."

Upvotes: 1

Related Questions