Nested function in Python class - python-3.x

i have a little "basic understanding" Python problem.
So let me explain my problem.
At first a very simple code snippet.
class Revert:
__sentence = ""
def __init__(self, sentence: str):
self.__sentence = sentence
def get_sentence(self):
return self.__sentence
def revert_sentence(self):
return self.__sentence[::-1]
if __name__ == '__main__':
print(Revert("Stackoverflow").get_sentence())
print(Revert("Stackoverflow").revert_sentence())
So this show normal function calling of python functions.
But how can i transform this code so i can call the revert function like this:
print(Revert("Stackoverflow").get_sentence().revert_sentence())
Maybe I'm miss the forest through the trees. But I didn't get it how to do this.
I already tried to solve the problem with innermethods but this didn't work for me
...
def get_sentence(self):
def revert_sentence():
self.revert_sentence()
return self.__sentence
...
Many thanks in advance

Implement __str__ to return the actual string. Then in the existing methods, return the object. This way you can chain. But when print is applied to it, that __str__ method will kick in:
class Revert:
__sentence = ""
def __init__(self, sentence: str):
self.__sentence = sentence
def get_sentence(self):
return self
def revert_sentence(self):
return Revert(self.__sentence[::-1])
# Some more such methods ...
def upper(self):
return Revert(self.__sentence.upper())
def first(self, count):
return Revert(self.__sentence[:count])
def dotted(self):
return Revert(".".join(self.__sentence))
# For getting a string
def __str__(self):
return self.__sentence
print(Revert("Stackoverflow").get_sentence().revert_sentence())
print(Revert("Stackoverflow")
.revert_sentence()
.first(8)
.upper()
.revert_sentence()
.first(4)
.dotted()) # "O.V.E.R"
Note that now the .get_sentence() method is not really doing much, and you can always strip it from a chain.

Here You go:
class Revert:
__sentence = ""
def __init__(self, sentence: str):
self.__sentence = sentence
def get_sentence(self):
return self.__sentence
def revert_sentence(self):
# It's important to know that you are making changes in the same instance of the object
self.__sentence = self.__sentence[::-1]
return self
def pseudo_revert(self):
# Return a new object with reverted string, but this instance still has original string intact.
return Revert(self.__sentence[::-1])
if __name__ == '__main__':
r1 = Revert("Stackoverflow")
r2 = Revert("Stackoverflow")
print(r1.get_sentence()) # Stackoverflow
print(r1.revert_sentence().get_sentence()) # wolfrevokcatS
print(r1.get_sentence()) # wolfrevokcatS
print(r2.get_sentence()) # Stackoverflow
print(r2.pseudo_revert().get_sentence()) # wolfrevokcatS
print(r2.get_sentence()) # Stackoverflow
Hope this helps you understand the object, instance of an object, and method of object distinctly.

Related

How to work with a python collections.deque of custom class instances?

I am trying to utilize python deque from collections module where the element inside the deque is a custom class instances. I want to know how I can erase/delete an object? Can I use the builtin methods like deque.remove(element) if yes, how? How would it find my custom object?
class Buffer(object):
""" """
def __init__(self, name, size_total, size_in_cache):
self.name = name
self.size_total = size_total
class Storage(object):
"""
"""
def __init__(self, capacity):
self.CAPACITY = capacity
self.contents = collections.deque()
self.capacity_used = 0
def push_to_contents(self, buffer_name, buffer_size):
buf = Buffer(buffer_name, buffer_size)
self.contents.appendleft(buf)
def delete_from_contents(self, buffer_name)
""" HOW??
How can I use self.contents.remove() here>
"""
The way collections.deque.remove operates is by comparing the argument to each item in the deque. If it finds something which is equal to the argument, it removes it. Otherwise, it raises a ValueError.
Since a Buffer object, as you've implemented it, doesn't know how to compare itself with other objects, Python defaults (using the object parent class) to comparing the id values.
However, if you were to implement the __eq__ method for your class, you could accomplish what you're looking for.
E.g.,
def __eq__(self, other):
if isinstance(other, Buffer):
return self.name == other.name and self.size_total == other.size_total
elif isinstance(other, str):
return self.name == other
else:
return NotImplemented
EDIT:
This is all fine and good if you're using Python 3. In Python 2, you have to implement __ne__ ("not equal") as well. It can be as simple as
def __ne__(self, other):
return not self == other
Python 3 takes care of this for you automatically.

Unit test initializes class without calling parameters, how do I get the parameters into the class?

So I'm working with Linked Lists in python, and the UnitTest our professor gave us calls C = Course(), but asserts the values after. This is what they use to grade, but I can't find a way to call the class then wait for the variables, and apply them to the class without parameters so it doesn't crash. Thoughts? Am I missing something obvious?
Tried to only include the relevant code. If people need full code for clarification or just for kicks/giggles let me know.
import courselist
import course
def load_data(data):
with open(data) as f:
for line in f:
dline = line.strip().split(",")
C = course.Course(dline[0],dline[1],dline[2],dline[3])
course_list = courselist.CourseList()
course_list.insert(C)
return course_list
def main():
data = "data.txt"
full_list = load_data(data)
print(full_list.__str__())
main()
class Course:
def __init__(self, c_num, c_name, c_hour, c_grade):
self.c_num = c_num
self.c_name = c_name
self.c_hour = c_hour
self.c_grade = c_grade
self.next = None
class TestEmptyCourse(unittest.TestCase):
def test_course_creation(self):
# make sure that an empty course is correct
c = Course()
self.assertEqual(c.name(), "")
self.assertEqual(c.number(), 0)
self.assertEqual(c.credit_hr(), 0.0)
self.assertEqual(c.grade(), 0.0)
self.assertEqual(c.next, None)
I was missing something obvious... Hangs head in shame For those interested here's how to fix it. It's always when you ask for help that you get it just in time to look like an idiot haha. Thanks to those to checked it out. If someone has an alternate solution I'll be sure to upvote you
class Course:
def __init__(self, num=0, cname="", c_hr=0.0, cgrade=0.0, next=None):
self.num = num
self.cname = cname
self.c_hr = c_hr
self.cgrade = cgrade
self.next = None
def number(self):
return int(self.num)
def name(self):
return str(self.cname)
def credit_hr(self):
return self.c_hr
def grade(self):
return self.cgrade
def __str__(self):
return f"cs{self.num} {self.cname} Grade:{self.cgrade} Credit Hours: {self.c_hr}"

Can we skip explicit object creation in Python

When I do not crate object for CP class, the operations are not captured. I am referring to the code below, Can somebody help me understand why we need obj creation in this case
from abc import ABC, abstractmethod
class P(ABC):
def __init__(self):
super().__init__()
self._pre_map = {}
self._pre_order = []
def set_pre(self, tag_value):
index = len(self._pre_map)
print(index)
self._pre_map[index] = tag_value
self._pre_order.append(index)
def execute(self):
pass
class CP(P):
def __init__(self):
super().__init__()
def execute(self):
self.prnt()
def prnt(self):
print (self._pre_map)
print (self._pre_order)
#Working
print("\n++++++++ working")
obj = CP()
obj.set_pre("test string added")
obj.execute()
#Not Working
print("\n+++++++ not working")
CP().set_pre("test string added")
CP().execute()
It produces,
++++++++working
0
{0: 'test string added'}
[0]
+++++++not working
0
{}
[]
When you call the class the second time with CP.execute(), you have created a completely new instance of the CP class. It is not going to have the text string you specified.
If you actually wanted it to print the values like the working one you can make the functions return self after each call in the P class. If you did that you could do something like this.
from abc import ABC, abstractmethod
class P(ABC):
def __init__(self):
super().__init__()
self._pre_map = {}
self._pre_order = []
def set_pre(self, tag_value):
index = len(self._pre_map)
print(index)
self._pre_map[index] = tag_value
self._pre_order.append(index)
##need to return self here
return self
def execute(self):
pass
class CP(P):
def __init__(self):
super().__init__()
def execute(self):
self.prnt()
def prnt(self):
print (self._pre_map)
print (self._pre_order)
#Working
print("\n++++++++ working")
obj = CP()
obj.set_pre("test string added")
obj.execute()
#Not Working
print("\n+++++++ not working: but now working after returning self in the P class")
CP().set_pre("test string added").execute()
++++++++ working
0
{0: 'test string added'}
[0]
+++++++ not working: but now working after returning self in the P class
0
{0: 'test string added'}
[0]
This would print the result you want.
The reason for the difference is the fact that in the first one, you are creating an instance, and using that instance the whole way through, whereas in the second one, you are using two different instances of your class.
The two different instances cannot share their attributes, so you are unable to recall what happened. If you really don't want to use a dedicated variable, change your P class to look like this:
class P(ABC):
...
def set_pre(self, tag_value):
index = len(self._pre_map)
print(index)
self._pre_map[index] = tag_value
self._pre_order.append(index)
return self
...
And use CP().set_pre("test string added").execute()

Mocking a method's return value does not work

While testing the create_response method, I cannot seem to mock the return value of the get_external_response method.
/foo/response
from abc import ABCMeta, abstractmethod
def create_response(url, type):
query = create_query(url, type)
external_response = get_external_response(query) <-- what I want to mock
return external_response
def create_query(url, type):
cc = MyFactory
return cc.get_concrete_class(url, type)
def get_external_response(cc):
return cc.make_query()
class MyAbstractClass(metaclass=ABCMeta):
def __init__(self, url, type):
self.url = url
self.type = type
self.query = self.make_query()
#abstractmethod
def make_query(self):
pass
class MyFactory:
#staticmethod
def get_concrete_class(url, type):
if type == 'A':
return MyClass(url, type)
else:
print("not valid type")
class MyClass(MyAbstractClass):
def __init__(self, url, type):
super().__init__(url, type)
def make_query(self):
return self.url + self.type
if __name__ == '__main__':
result = create_response('www.stackoverflow.com', 'A')
print(result)
If I run the above, I get the expected www.stackoverflow.comA.
But if try to mock the return value of get_external_response, it does not seem to do anything: it still returns www.stackoverflow.comA and the assertion below fails.
/foo/test_response
from foo.response import create_response
import pytest
from unittest import mock
def test_create_response():
mock_external_response = mock.Mock()
mock_external_response.create_flask_response.return_value = 'www'
result = create_response('www.stackoverflow.com', 'A')
assert result == 'www'
I do not understand why the return value is not set since when the create_response is called, it will eventually reach the point of calling the create_flask_response which, if I am not mistaken, should return www given that I have mocked it.
Can someone explain what I am doing wrong?
I noticed that you are creating a Mock object inside of a function, but are not actually using the Mock.
It looks like you need to patch the function where it is used to use the Mock.
/foo/test_response
#mock.patch('foo.response.get_external_response')
def test_create_response(mock_get_external_reponse):
mock_get_external_response.return_value = 'www' # This is a Mock object, but will be used as a patch over the actual function where it is used
result = create_response('www.stackoverflow.com', 'A')
assert result == 'www'
Quick links to relevant documentation sections for convenience:
Intro to mocking and patching: https://docs.python.org/3/library/unittest.mock.html#quick-guide
Patch specifically here: https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch

Why generator object is not return generator?

I have simple iteration object that use generator. But __iter__() method of Obj() can not get iterator from Generator().__init__()
Live demo is here.
#!/usr/bin/env python3
class Obj():
def __init__(self, word):
self.word = word
def __iter__(self):
return Generator(self.word)
class Generator():
def __init__(self, word):
for l in word:
yield l
obj = Obj('qwe')
it = iter(obj)
print(it.__next__())
print(it.__next__())
print(it.__next__())
I expect console output to be 'qwe'.
__init__ cannot return or yield a non-None value. With what you have, Generator is not a generator object, but Obj is. Obj.__iter__ is the method that should be yielding values, and you can probably get rid of the Generator class altogether, unless it is used for other things that you haven't shown in your posted snippet.
class Obj():
def __init__(self, word):
self.word = word
def __iter__(self):
for l in self.word:yield l
obj = Obj('qwe')
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())

Resources