Using Python classes for encapsulation, not instantiation - python

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...

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

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.

Related

Python 3 - Does the direct manipulation of a class' attribute override the same attribute for its objects making the attribue purely static?

While learning about how classes work in Python I came across a class definition example which behaved kind of strangely in my eyes.
The purpose of the example was to demonstrate how the behaviour of a static variable can be achieved in Python. The example was written as follows:
class MemberCounter:
members = 0
def init(self):
MemberCounter.members += 1
m1 = MemberCounter()
m1.init()
m2 = MemberCounter()
m2.init()
after setting up the class and creating the objects, I printed the values of the 'members' attribute. These were the results:
MemberCounter.members = 2
m1.members = 2
m2.members = 2
And that's when I got confused. While I was expecting for 'MemberCounter.members = 2' the two other results made no sense to me - why would both of 'm1' and 'm2' objects' 'members' value be equal to 2? I thought that both of the values should have been 0 - if the only attribute that was chaged is the 'members' attribute which was attached to the MemberCounter class why would it cause any change to the own unique 'members' value of each of the class' objects. It looks like the fact that the 'members' attribute is addresed like 'MemberCounter.members += 1' in the init() function of each object, completely overrides the unique values which m1.members and m2.members refer to and redirects their pointers to the MemberCounter.members value making all the three pointers point at the same value
==> m1.members = m2.members = MemberCounter.members.
Moreover, I have tried defining the class in an opossite way (Increasing self.members instead of MemberCounter.members):
class MemberCounter:
members = 0
def init(self):
self.members += 1
m1 = MemberCounter()
m1.init()
m2 = MemberCounter()
m2.init()
This definition yielded logical results (which got me curious about the above mentioned strange behaviour even more):
MemberCounter.members = 0
m1.members = 1
m2.members = 1
In short, I was curious about why the first class definition behaves in such a strange way? Why the mere 'MemberCounter.members += 1' statement completely erased 'm1.members' and 'm2.members' own unique value and made it equal to the MemberCounter.members value.
I hope I was able to clearly present my problem and I will be extremly happy to get an insight about this strange behaviour :)
That you can read a static attribute with instance.attribute notation as alternative to the more natural class.attribute notation, is an intended feature in Python.
From the documentation:
Both static data and static methods (in the sense of C++ or Java) are supported in Python.
For static data, simply define a class attribute. To assign a new
value to the attribute, you have to explicitly use the class name in
the assignment:
class C:
count = 0 # number of times C.__init__ called
def __init__(self):
C.count = C.count + 1
def getcount(self):
return C.count # or return self.count
c.count also refers to C.count for any c such that
isinstance(c, C) holds, unless overridden by c itself or by some
class on the base-class search path from c.__class__ back to C.
Caution: within a method of C, an assignment like self.count = 42
creates a new and unrelated instance named “count” in self’s own dict.
Rebinding of a class-static data name must always specify the class
whether inside a method or not:
C.count = 314
The paragraph just below the first code block explains your doubts. The "Caution" paragraph explains what you found logical.

Alternating the use of classes/globals with closures in Python

I came across closures in python, and I've been tinkering around the subject.
Please Correct me if I'm wrong here, but what I understood for when to use closures (generally) is that it can be used as a replacement of small classes (q1) and to avoid the use of globals (q2).
Q1: [replacing classes]
Any instance created from the datafactory class will have it's own list of data, and hence every appending to that object's list will result in an incremental behavior. I understand the output from an OO POV.
class datafactory():
def __init__(self):
self.data = []
def __call__(self, val):
self.data.append(val)
_sum = sum(self.data)
return _sum
incrementwith = datafactory()
print(incrementwith(1))
print(incrementwith(1))
print(incrementwith(2))
OUTPUT:
1
2
4
I tried replacing this with a closure, it did the trick, but my understanding to why/how this is happening is a bit vague.
def data_factory():
data = []
def increment(val):
data.append(val)
_sum = sum(data)
return _sum
return increment
increment_with = data_factory()
print(increment_with(1))
print(increment_with(1))
print(increment_with(2))
OUTPUT:
1
2
4
What I'm getting is that the data_factory returns the function definition of the nested increment function with the data variable sent along as well, I would've understood the output if it was something like this:
1
1
2
But how exactly the data list persists with every call?
Shouldn't variables defined in a function die after the function finishes execution and get regenerated and cleared out with the next fn call?
Note: I know that this behavior exists normally in a function defined with default parameters like def func(val, l = []): where the list will not be cleared on every fn call, but rather be updated with a new element/append, which is also something that I do not fully understand.
I would really appreciate an academic explanation to what happens in both scenarios (OO and closures).
Q2: [replacing use of global]
Is there a way using closures to increment the following variable without using globals or a return statement ?
a = 0
print("Before:", a) # Before: 0
def inc(a):
a += 1
print("After:", a) # After: 0
Thank you for your time.
For the first question, I found after some digging that passing mutables as default parameters isn't really a good move to make:
https://florimond.dev/blog/articles/2018/08/python-mutable-defaults-are-the-source-of-all-evil/#:~:text=of%20this%20mess.-,The%20problem,or%20even%20a%20class%20instance.

Designing a black box python [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I want to have a black box in python where
The input is a list A.
There is a random number C for the black box which is randomly selected the first time the black box is called and stays the same for the next times the black box is called.
Based on list A and number C, the output is a list B.
I was thinking of defining this black box as a function but the issue is that a function cannot keep the selected number C for next calls. Note that the input and output of the black box are as described above and we cannot have C also as output and use it for next calls. Any suggestion?
Make it a Class so C will persist.
class BlackBox():
def __init__(self):
self.C = rand.randint(100)
etc...
As a side note, using some pretty cool Python functionality...
You can make objects of this class callable by implementing __call__() for your new class.
#inside the BlackBox class
def __call__(self, A):
B = []
#do something to B with A and self.C
return B
You can then use this in your main code.
bb = BlackBox()
A = [1, 2, 3]
B = bb(A)
the issue is that a function cannot keep the selected number C for next calls.
This may be true in other languages, but not so in Python. Functions in Python are objects like any other, so you can store things on them. Here's a minimal example of doing so.
import random
def this_function_stores_a_value():
me = this_function_stores_a_value
if 'value' not in me.__dict__:
me.value = random.random()
return me.value
This doesn't directly solve your list problem, but it should point you in the right direction.
Side note: You can also store persistent data in optional arguments, like
def this_function_also_stores_a_value(value = random.random()):
...
I don't, however, recommend this approach because users can tamper with your values by passing an argument explicitly.
There are many ways to store persistent data for a function. They all have their uses, but in general, the ones that come first are useful more often than the ones that come later. (To keep things shorter, I'm solving a slightly simpler problem than the one you asked about, but it should be obvious how to adapt it.)
Instance attribute
class BlackBox:
def __init__(self):
self.C = rand.randint(100)
def check(self, guess):
return (guess - self.C) / abs(guess - self.C)
Now you can create one or more BlackBox() instances, and each one has its own random number.
Closure variable
def blackbox():
C = rand.random()
def check(guess):
return (guess - C) / abs(guess - C)
return check
Now, you can create one or more check functions, and each one has its own random number. (This is dual to the instance variable—that is, it has the same capabilities—but usually one or the other is more readable.)
Global variable
def makeblackbox():
global C
C = random.randint(100)
def check(guess):
return (guess - C) / abs(guess - C)
This way, there's only a single blackbox for the entire program. That's usually not as good a design, which is one of the reasons that "globals are bad". Plus, it's polluting the global namespace with a C variable that means nothing to anyone but the check function, which is another one of the reasons that "globals are bad".
Function attribute
def makeblackbox():
check.C = random.randint(100)
def check():
return (guess - check.C) / abs(guess - check.C)
This is equivalent to a global in that you can only ever have one black box, but at least the variable is hidden away on the check function instead of polluting the global namespace.
Class attribute
class BlackBox:
C = rand.randint(100)
#staticmethod
def check(guess):
return (guess - BlackBox.C) / abs(guess - BlackBox.C)
This is again equivalent to a global variable without polluting the global namespace. But it has a downside over the function attribute—you're creating a class that has no useful instances is often misleading.
Class attribute 2
class BlackBox:
C = rand.randint(100)
#classmethod
def check(cls, guess):
return (guess - cls.C) / abs(guess - cls.C)
This is different from the last three in that you can create new blackboxes by creating subclasses of BlackBox. But this is very rarely what you actually want to do. If you want multiple persistent values, you probably want instances.
Since you are asking in the comments.
This is probably not recommended way but it's easy and works so I'll add it here.
You can use global variable to achieve your goal.
import random
persistant_var = 0
def your_func():
global persistant_var
if persistant_var:
print('variable already set {}'.format(persistant_var))
else:
print('setting variable')
persistant_var = random.randint(1,10)
your_func()
your_func()
Output:
setting variable
variable already set 7
Hope this is clear.

Nested classes: Accessing the methods of the outer class from the inner one

Suppose you have two classes, A and B. Class B is defined inside the class A. I want to access the variables and methods of the outer class while inside the inner class. The code here is a toy example but has the essentials of what I want to demonstrate:
class A:
a = 'even'
b = 'odd'
class B:
def __init__(self, n):
if n%2 == 0: self.num = a
if n%2 == 1: self.num = b
self.description = A.desc()
def __getitem__(self, i):
return self.B(i)
def desc(self):
return a + '-' + b
>>> c = A()
>>> d = c[4]
>>> TypeError: unbound method desc() must be called with A instance as first argument (got nothing instead)
Here the method desc does some work on the variables of the class A and produces output. Class A is initialized correctly and you can access the variables a and b, even from the inner scope, given that you don't define the description variable. However, I cannot find a way to call the outer scope class methods desc. Is it possible to use the method desc in B without instantiating class A?
Explanation on why I use such a pattern:
Variables a and b in my program are rather big. I only need to initialize them once. In addition, I don't want these variables to float around in the program but to be only accessible to the inner class. Adding to all these is the fact that I can use the A.__getitem__ to extract 'slices' of the big data when needed. So the outer class provides me with hiding/encapsulation of the data, the indexing operator (through __getitem__) and all the routines required for extraction of slices of data (here the method desc. The inner class, B, provides the bundling of useful information from the big data for each index. This, most likely, is not the optimal design for achieving the described task. I am open and eager to hear your opinion regarding the alternative patterns.
I can't see any reason for you to be using classes here, let alone nested ones. In any case, there is almost never a reason to nest classes in Python, since inner classes don't get any special access to the outer class.
However if you want to allow anything to access a method without instantiating the object, you can make it a classmethod:
#classmethod
def desc(self):
return a + '-' + b
But I can't see why you would do any of this. Also, nothing here is a closure.

How to tell when a method is called for first time of many

I would like to be able to tell when a method has been called for the first time. I primarily need this for when I am printing out to a delimited file, and if it is the first iteration, I would like to print a header before the actual information. This is what I normally do:
def writeFile(number, count):
if count == 1:
print('number')
print(str(count))
else:
print(str(count))
count = 1
for i in range(10):
writeFile(i, count)
count += 1
This provides the following output:
number
1
2
3
4
5
6
7
8
9
10
Though this achieves the goal I am after, I am curious as to if there is a better/more efficient way of doing this. Is there some way to detect if a method has been called for the first time without having to pass an additional argument to it?
Thank you,
There are multiple ways to do this. Here are three.
First:
firstRun=True
def writeFile(number):
global firstRun
if firstRun:
print('number')
firstRun=False
print(str(number))
for i in range(10):
writeFile(i)
Second:
def writeFile(number):
print(str(number))
for i in range(10):
if not i:
print('number')
writeFile(i)
Third:
for i in range(10):
print(('' if i else 'number\n')+str(i))
I'm assuming this is just a test problem meant to indicate cases where function calls initialize or reset data. I prefer ones that hide the information from the calling function (such as 1). I am new to Python, so I may be using bad practices.
You could write the header to the file before you call the function. That would negate your need for the if statements. I'm a basic level programmer, but this seems logical to me. For example:
def writeFile(count):
print(str(count))
print('number')
for i in range(10):
writeFile(i)
This is a bit more deep respect to the other answers but I prefer it since it uses the OOP-ness of Python, the idea is to assign to the function itself the "called" variable: this can be done since everything in Python is an object (even a function inside its own scope).
The concept can be extended also to functions defined in other scopes - besides class scope - as well.
class SampleClass:
def sample(self, *args, **kwargs):
try:
if self.__class__.sample.called:
# do what you have to do with the method
print("normal execution")
except AttributeError:
# do what you have to do with the first call
print("first call")
self.__class__.sample.called = True
self.__class__.sample(self, *args, **kwargs)
Example:
>>>SampleClass().sample()
first call
normal execution
>>>SampleClass().sample()
normal execution

Categories

Resources