eSurfsnake
eSurfsnake

Reputation: 689

Using Python classes for encapsulation, not instantiation

I have run across a few examples of Python code that looks something like this:

class GiveNext :

    list = ''

    def __init__(self, list) :
        GiveNext.list = list

    def giveNext(self, i) :
        retval = GiveNext.list[i]
        return retval



class GiveABCs(GiveNext):

    i = -1

    def _init__(self, list) :
        GiveNext.__init__(self, list)

    def giveNext(self):
        GiveABCs.i += 1
        return GiveNext.giveNext(self, GiveABCs.i)


class Give123s(GiveNext):

    i = -1

    def _init__(self, list) :
        GiveNext.__init__(self, list)

    def giveNext(self):
        Give123s.i += 1
        return GiveNext.giveNext(self, Give123s.i)


for i in range(3):
    print(GiveABCs('ABCDEFG').giveNext())
    print(Give123s('12345').giveNext())

the output is: A 1 B 2 C 3

If I were more clever, I could figure out how to put the string literals inside the constructor...but that is not crucial right now.

My question is on the use of classes this way. Yes, an instance of the class gets created each time that that the call within the print() gets made. Yet the i's are 'permanent' in each class.

This strikes me as less of an object-oriented approach, and more of a way of using classes to accomplish encapsulation and/or a functional programming paradigm, since the instances are entirely transitory. In other words, an instance of the class is never instantiated for its own purposes; it is there only to allow access to the class-wide methods and variables within to do their thing, and then it is tossed away. In many cases, it seems like the class mechanism is used in a back-handed way, in order to leverage inheritance and name resolution/spacing: an instance of the class is never really required to be built or used, conceptually.

Is this standard Python form?

Bonus question: how would I put the string literals inside each class declaration? Right now, even if I change the _init__ for GiveABCs to

GiveNext.__init__(self, 'wxyz')

it completely ignores the 'wxyz' literal, and uses the 'ABCDEF' one - even though it is never mentioned...

Upvotes: 1

Views: 271

Answers (2)

Guillaume
Guillaume

Reputation: 6009

This code in the OP is an incredible amount of crap. Not only it is long, unreadable, misuses OO features, and does not use Python features at all (an iterator being a standard Python feature). Here is a suggestion for a more Pythonist approach:

giveABCs = iter('ABCDEFG')
give123s = iter('12345')
for i in range(3):
    print(next(giveABCs))
    print(next(give123s))

About your bonus question: I guess you are modifing the _init__() method of GiveABCs and Give123s. It is normal that whatever code you put in there has no effect, because the Python constructor is __init__() (with 2 leading underscores, not 1). So The constructor from GiveNext is not overloaded.

Upvotes: 1

Eric Duminil
Eric Duminil

Reputation: 54223

Please don't learn Python with this code. As mentioned by others, this code goes against many Python principles.

One example: list is a Python builtin type. Don't overwrite it, especially not with a string instance!

The code also mixes class and instance variables and doesn't use super() in subclasses.

This code tries to simulate an iterator. So simply use an iterator:

give_abcs = iter('ABCDEFG')
give_123s = iter('12345')

for _ in range(3):
    print(next(give_abcs))
    print(next(give_123s))

# A
# 1
# B
# 2
# C
# 3

If you really want to fix the above code, you could use:

class GiveNext :
    def __init__(self, iterable) :
        self.i = - 1
        self.iterable = iterable

    def giveNext(self) :
        self.i += 1
        return self.iterable[self.i]

giveABCs = GiveNext('ABCDEFG')
give123s = GiveNext('12345')

for _ in range(3):
    print(giveABCs.giveNext())
    print(give123s.giveNext())

It outputs:

A
1
B
2
C
3

Upvotes: 1

Related Questions