Why does Pycharm Console do this with my __repr__ method? - python

class B:
def __init__(self):
print('boo!')
self.a = []
def __repr__(self):
print(len(self.a))
ret = ''
for a in self.a:
ret += str(a)
return ret
The following is copy pasted from the Pycharm Console using python 3.8.6 and IPython 7.31.0:
>>> b = B()
boo!
0
0
>> b
Out[4]: 0
0
0
0
0
0
0
0
0
0
0
>> 2 + 3
Out[5]: 5
0
0
0
0
0
0
0
0
0
0
This does not happen in python REPL or iPython running in the cmd (see comments).
Why is this happening?

You're only showing and using B in your code, so we'll use just that.
class B:
def __init__(self):
print('boo!')
self.a = []
def add_a(self, a):
self.a.append(a)
def __repr__(self):
print(len(self.a))
ret = ''
for a in self.a:
ret += str(a)
return ret
When you run just B() (a call expression), this is the sequence of things that happens (in python and ipython):
create new instance with __new__ (inherited from object)
initialise the instance with __init__, which prints boo! and sets the a attribute to an empty list []
display the instance you've just created with __repr__, which in your case:
prints 0, the length of a attribute
set a local ret variable to ''
iterates through the a attribute which is empty
returns returns '' from ret
However, step 3. applies only to running expressions B(), the python interactive shell runs the expression and displays their result.
The b = B() assignment statement should not trigger a "display result" event (and will not in python, IDLE or ipython).

Related

Hidden calls to __getattribute__

I'm trying to write a mixin that increments a counter every time a public attribute is read. My implementation below seems to have a hidden increment that I cannot find. From the commented out print statements I can identify it as occurring in the self._read_count += 1 statement. What am I misunderstanding?
class CounterMixin:
def __init__(self):
self._read_count = 0;
super().__init__();
def _inc_read_count(self):
# print(f'Pre-inc: {self._read_count}');
self._read_count += 1;
# print(f'Post-inc: {self._read_count}');
#property
def read_count(self):
self._inc_read_count();
return self._read_count;
def __getattribute__(self, attr):
if attr[0] != '_':
# print(f'Counting {attr}');
self._inc_read_count();
else:
# print(f'Not counting {attr}');
pass;
return super().__getattribute__(attr);
class Bar(CounterMixin):
pass
foo = Bar();
print(foo.read_count);
print('---');
foo.x = 1;
print(foo.read_count);
print('---');
_ = foo.x;
print(foo.read_count);
Expected output:
1
---
2
---
4
Actual output:
2
---
4
---
7
You're incrementing self._read_count twice when you access read_count, once in __getattribute__ and once in the property getter.

Using parameters to pass values between functions

I am currently having an issue, as i am relatively new to python , it might be a very easy solution for others.
I want to pass a parameter between both functions 'eg1' and 'eg2', there is a common number the user will input (example:10) then 'eg1' will add 1 to it and 'eg2' will take the final value of 'eg1' and add 1 more to it, (example: 10 will become 11 then 12)
It is troubling me because this keeps popping up:
Traceback (most recent call last):
File "example.py", line 69, in <module>
eg2(a)
File "example.py", line 63, in eg2
b = a.d
AttributeError: 'int' object has no attribute 'd'
I can't seem to find my mistake.
class Helper:pass
a = Helper()
def one(a):
d = a
d += 1
print d
def two(a):
b = a.d
b += 1
print b
print
print ("Please enter a number.")
a = int(input('>> ')
print
one(a)
print
two(a)
Reference for parameter passing:
Python definition function parameter passing
'Print' with nothing means to leave an empty line, for me
I messed up the title, fixed.
Since you are already using a class, you can pass the number you want to increment twice as an instance attribute, and call your increment functions on that attribute. This will avoid passing the updated value after calling one in the method two
Calling one and then two makes sure that two is working on the updated value after calling one.
class Helper:
# Pass num as parameter
def __init__(self, num):
self.num = num
# Increment num
def one(self):
self.num += 1
# Increment num
def two(self):
self.num += 1
h = Helper(10)
h.one()
print(h.num) # 11
h.two()
print(h.num) # 12
Based on your comments, here's one way to get the result. I am using python3:
class Helper:
d = None
cl = Helper()
def one(a):
cl.d = a
cl.d += 1
return cl.d
def two():
cl.d += 1
return cl.d
print ("Please enter a number.")
a = int(input('>> '))
print(one(a))
print(two())

Python - Log the things a string has previously been

If this is my code:
x = 1
x = 2
x = 3
How can I “log” the things x has been and print them? If my explanation was dumb, then here’s what I expect:
>>> # Code to print the things x has been
1, 2, 3
>>>
How can I achieve this?
Since assignment overwrites the value of the object (in your example 'x'), it is not possible to do exactly what you want. However, you could create an object, of which the value can be changed and its history remembered. For example like this:
#!/usr/bin/env/python3
class ValueWithHistory():
def __init__(self):
self.history = []
self._value = None
#property
def value(self):
return self._value
#value.setter
def value(self, new_value):
self.history.append(new_value)
self._value = new_value
def get_history(self):
return self.history
def clear_history(self):
self.history.clear()
def main():
test = ValueWithHistory()
test.value = 1
print(test.value)
test.value = 2
print(test.value)
test.value = 3
print(test.value)
print(test.get_history())
if __name__ == '__main__':
main()
This prints:
1
2
3
[1, 2, 3]
Of course, you could also use a set instead of a list to only remember each unique value once, for example.
You can order a second thread to observe the string and print the changes:
from threading import Thread
def string_watcher():
global my_string
global log
temp = ''
while True:
if my_string != temp:
log.append(my_string)
temp = my_string
t = Thread(target=string_watcher, daemon=True)
t.start()
This checks weather the string „my_string“ was manipulated and appends it to the list „log“, if it has been changed. With this you should be able to perform
Print(log)
At any moment of the runtime

While evaluating length of a list that is defined as a class

For example, I got a class named stack,
class stack: #Will be used for probable files!
def __init__(self):
self.data = []
def add(self, element):
self.data.append(element)
def number_of_elements(self):
return len(self.data)
def stackType(self):
if self.number_of_elements == 0:
return 0
elif self.number_of_elements == 1:
return 1
else:
return -1
I then do this:
foo = stack()
print foo.stackType()
I get -1 however I was expecting a return of 1
Why is it so and how can I handle with it?
That's because you did not call the call the method self.number_of_elements; you merely tested to see if it equalled 0 or 1.
Modify your code to actually call the method using this syntax: self.number_of_elements() [notice the use of () to call the method]:
def stackType(self) :
if self.number_of_elements() == 0 :
return 0
elif self.number_of_elements() == 1 :
return 1
else :
return -1
You could also have written it like this:
def stack_type(self):
n = self.number_of_elements()
return -1 if n > 1 else n
which would be an improvement because number_of_elements() will be called once only. In your code the method could be called twice. I renamed the function to be consistent with the Python method naming conventions set out in PEP8.
Because self.number_of_elements is not the same as self.number_of_elements()!
The former is a reference to the function, the latter is a call to the function actually calculating the length of your stack.
self.number_of_elements is a function, so its value is neither zero nor 1

What's the equivalent of Ruby's class ##variable in Python?

In Ruby 1.9, I can use its class variable like the following:
class Sample
##count = 0
def initialize
##count += 1
end
def count
##count
end
end
sample = Sample.new
puts sample.count # Output: 1
sample2 = Sample.new
puts sample2.count # Output: 2
How can I achieve the above in Python 2.5+ ?
class Sample(object):
_count = 0
def __init__(self):
Sample._count += 1
#property
def count(self):
return Sample._count
The use is a bit different from Ruby; e.g. if you have this code in module a.py,
>>> import a
>>> x = a.Sample()
>>> print x.count
1
>>> y = a.Sample()
>>> print x.count
2
having a Sample.count "class property" (with the same name as the instance property) would be a bit tricky in Python (feasible, but not worth the bother IMHO).

Categories