Why setting some attributes of a class is not possible? - python

I tried to create my own dummy response inherited from requests.Response. It would add an extra attribute and overwrite an existing one:
import requests
class MyResponse(requests.Response):
def __init__(self):
super().__init__()
self.hello = "world"
self.ok = False
print(vars(MyResponse()))
Adding self.hello is fine, but when I want to force self.ok to a value I get:
Traceback (most recent call last):
File "C:/Users/yop/.PyCharm2019.2/config/scratches/scratch.py", line 11, in <module>
print(vars(MyResponse()))
File "C:/Users/yop/.PyCharm2019.2/config/scratches/scratch.py", line 9, in __init__
self.ok = False
AttributeError: can't set attribute
Why are there some attributes which cannot be set/overwritten?

ok is a property of requests.Response but it has no setter so it can't be set.
Instead, you can override it and always return False (or True or whatever you want):
class MyResponse(requests.Response):
def __init__(self):
super().__init__()
self.hello = "world"
#property
def ok(self):
return False
Alternatively, look at a proper mocking solution such as the mock module.

Related

Python library using parent method instead of overriden method

I have extended the Parent class with the Override class. I have have overridden the method() method to fix a bug that occurs in the Parent class. I fixed the said bug and this fix has been tested in the Override class.
I use the Override class through an External class. By testing the External class to see if the previous bug is fixed, I discovered that it is not and that the traceback does not go through the Override class.
class Parent():
def method(self, param):
# Bugged
do_stuff()
class Override(Parent):
def method(self, param):
# Fixed (tested)
param = fix_param(param)
super(Parent, self).method(param)
class External():
def processing():
# Same bug as in `Parent`
param = get_param()
obj = Override()
obj.method(param)
It seems to me that the External class uses the Parent.method() method instead of the Override.method() method. Any clue on how to fix it or on where this problem comes from?
I'm a beginner and have not been confronted inheritance a lot so, please, forgive my ignorance and my lack of experience.
EDIT
Test that fails in External :
import os
import collections
import envtpl
from acquisition.configargparse_confparser import StepConfigFileParser
from configparser_extended import ExtendedConfigParser
from unittest import TestCase
from acquisition.utils import set_custom_environment
class ConfigFileParserTestCase(TestCase):
def test_parse_extended(self):
# x = StepConfigFileParser("test_plugin_name", "test")
plugin_name = "test_plugin_name"
step_name = "test"
set_custom_environment(plugin_name, step_name)
config = os.environ.get('MFCONFIG', 'GENERIC')
filename = os.path.dirname(os.path.realpath(__file__)) + "/test.ini"
with open(filename, 'r') as stream:
config_parser = ExtendedConfigParser(
config=config, inheritance='im', interpolation=None)
content = stream.read()
config_parser.read_string(content) # Fails here
section = "step_%s" % step_name
res = collections.OrderedDict()
for key in config_parser.options(section):
if not key.startswith('arg_'):
continue
res[key.replace('arg_', '', 1)] = envtpl.render_string(
config_parser.get(section, key))
self.assertEqual(res, {"venom": "snake", "revolver": "ocelot"})
Overridden method :
read_string() in https://github.com/thefab/configparser_extended/blob/master/configparser_extended/ecp.py line 573
Parent method :
read_string() from configparser
(https://docs.python.org/3/library/configparser.html#configparser.ConfigParser.read_string)
test.ini
[step_test]
arg_venom=snake
arg_revolver=ocelot
liquid=snake
Error :
ERROR: test_parse_extended (tests.test_confparser.ConfigFileParserTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/florian/metwork/mfdata/src/acquisition/tests/test_confparser.py", line 39, in test_parse_extended
config_parser.read_string(content)
File "/opt/metwork-mfext/opt/python2/lib/python2.7/site-packages/backports/configparser/__init__.py", line 728, in read_string
sfile = io.StringIO(string)
TypeError: initial_value must be unicode or None, not str
Your code is not a complete working example, but it should do as you suggest.
Here is an example you can run to prove the point:
class Parent():
def method(self):
print('parent')
class Child(Parent):
def method(self):
print('child')
class Other():
def call_method(self):
c = Child()
c.method()
o = Other()
o.call_method()
That prints 'child', proving the Child class has overridden method(self).

super() does not work together with type()? : super(type, obj): obj must be an instance or subtype of type

In the code, new_type is a class created with members from class X and derived from class A. Any workaround for the TypeError?
class A:
def __init__(self):
pass
def B(self):
pass
def C(self):
pass
class X:
def __init__(self):
print(type(self).__bases__)
super().__init__()
def B(self):
self.B()
def Z(self):
pass
a = X()
print('ok')
new_type = type("R", ( A,), dict(X.__dict__))
some_obj = new_type()
Program output:
(<class 'object'>,)
ok
(<class '__main__.A'>,)
Traceback (most recent call last):
File "c:\Evobase2005\Main\EvoPro\dc\tests\sandbox.py", line 37, in <module>
some_obj = new_type()
File "c:\Evobase2005\Main\EvoPro\dc\tests\sandbox.py", line 27, in __init__
super().__init__()
TypeError: super(type, obj): obj must be an instance or subtype of type
In production code, class A does not exist either, but is created dynamically as well because it uses resources from a c++ library for class construction. hence the twisted code. ;)
EDIT This fails too.
class X:
def __init__(self):
print(type(self).__bases__)
super().__init__()
def Z(self):
pass
new_type = type("R", (object, ), dict(X.__dict__))
some_obj = new_type()
super() has two forms, two-argument form and zero argument form, quoting standard library docs:
The two argument form specifies the arguments exactly and makes the appropriate references. The zero argument form only works inside a class definition, as the compiler fills in the necessary details to correctly retrieve the class being defined, as well as accessing the current instance for ordinary methods.
The zero argument form will not work as it automatically searches the stack frame for the class (__class__) and the first argument and gets confused.
However, when you use the two-argument form of super(), the code works fine:
class A:
def __init__(self):
pass
class X:
def __init__(self):
print(type(self).__bases__)
super(self.__class__, self).__init__()
x = X()
R = type("R", (A,), dict(X.__dict__))
obj = R()
Output:
(<class 'object'>,)
(<class '__main__.A'>,)
You cannot use super(self.__class__, self) more than once though in the call hierarchy or you run into infinite recursion, see this SO answer.

Inherit and then override parent init

I have the following code:
# file1.py
class GenericScript(object):
def __init__(self):
self.start_time = time.time()
self.date_stem = str(timezone.now().date()).replace('-','')
self.script_name = os.path.basename(sys.argv[0]).replace('.py','')
self.file_name = None
self.log_file = None
self.integrity_field = '%s+%s' % (self.script_name, str(int(time.time())))
# file2.py
class RTUpdater(GenericScript):
def __init__(self):
self.integrity_field = '%s+%s' % (self.script_name, self.date_stem)
self.update_title_data = False
self.update_title_ranking = True
self.update_title_box_office = True
self.update_person_ranking = True
What I am trying to do is call RTUpdate() and get it to initialize all the items first in the parent Class and then add to those with its own __init__ method. However, when I call it, the self.integrity_field keep raising an error because self.script_name is not defined, meaning it's not first inheriting the parent __init__ variables. Here is how I'm calling it:
if __name__ == '__main__':
x = RTUpdater()
main(x)
>>> AttributeError: 'RTUpdater' object has no attribute 'script_name'
What am I doing wrong and how would I fix this?
You need to explicitly call the constructor of the parent class. Based on your inheritance of object I'm guessing you're using Python 2, so try this:
class RTUpdater(GenericScript):
def __init__(self):
super(RTUpdater, self).__init__()
# ...
If you are actually using Python 3 you can just use super().__init__().
You completly override __init__ as far as I see it. Just call super().__init__() in your inherited __init__ (so that the init of the parent will be run) and check if the error still persists.
Or if you are using Python 2 you need to fix the classes for the super call: super(RTUpdater, self).__init__().

nesting of properties vs setters and getters in python

class OurAtt():
def __init__(self):
self.Log = False
def setLog(self):
self.Log = True
def clearLog(self):
self.Log = False
class OurClass(object):
def __init__(self):
self.__OurAtt = OurAtt()
#property
def OurAtt(self):
return self.__OurAtt
#OurAtt.setter
def OurAtt(self, val):
raise Exception("can't modify the attribute" )
x = OurClass()
x.OurAtt.setLog()
print x.OurAtt.Log # print True
x.OurAtt.Log = False
print x.OurAtt.Log # sets to False Aim set this through function call x.OurAtt.setLog() I want to restrict the access, something like private variable.
Final aim is Log should be the attribute of OurAttr and should be protected by getter and setters or properties. Its like nesting of properties. and hierarchy should be maintained like object.OurAttr.Log
I researched and got the following link.
Python: multiple properties, one setter/getter
But It is not hitting my aim.
I am actually new to getter, setter and properties. Thanks in advance
I believe you are over-complicating the issue. If you want to prevent access to the attributes of OurAtt, the #property decorator should be used withing OurAtt. Instances of the OurAtt class will implement this protected-access behavior always, including when they are members of OurClass. You don't need to do anything with the #property decorator in OurClass unless you want to prevent modifying members of that class.
This, I think, does what you are trying to accomplish. It runs under 2.7 - if you are using an earlier version your mileage may vary.
class OurAttr(object):
def __init__(self):
self._log = False
#property
def log(self):
return self._log
#log.setter
def log(self, value):
raise AttributeError("Cannot set 'log' attribute directly.")
#log.deleter
def log(self):
raise AttributeError("Cannot delete 'log' attribute directly.")
def setLog(self):
self._log = True
print "Log is", self._log
def clearLog(self):
self._log = False
print "Log is", self._log
class OurClass(object):
def __init__(self):
self.OurAttr = OurAttr()
oc = OurClass()
oc.OurAttr.setLog()
oc.OurAttr.clearLog()
oc.OurAttr.log = False # Raises exception
Output is:
$ python2.7 test.py
Log is True
Log is False
Traceback (most recent call last):
File "test.py", line 33, in <module>
oc.OurAttr.log = False
File "test.py", line 11, in log
raise AttributeError("Cannot set 'log' attribute directly.")
AttributeError: Cannot set 'log' attribute directly.

exec to add a function into a class

So I've looked at similar questions, and I've found some solutions to this, but I can't quite figure out how to do this.
What I'm trying to do is add a method to a class from a string. I can do this with the setattr() method, but that won't let me use self as an attribute in the extra method. Here's an example: (and I apologize for the variable names, I always use yolo when I'm mocking up an idea)
class what:
def __init__(self):
s = 'def yolo(self):\n\tself.extra = "Hello"\n\tprint self.extra'
exec(s)
setattr(self,"yolo",yolo)
what().yolo()
returns this:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: yolo() takes exactly 1 argument (0 given)
and if s = 'def yolo():\n\tself.extra = "Hello"\n\tprint self.extra'
then I get this result:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 2, in yolo
NameError: global name 'self' is not defined
This essentially means that I cannot dynamically create methods for classes, which I know is bad practice and unpythonic, because the methods would be unable to access the variables that the rest of the class has access to.
I appreciate any help.
You have to bind your function to the class instance to turn it into a method. It can be done by wrapping it in types.MethodType:
import types
class what:
def __init__(self):
s = 'def yolo(self):\n\tself.extra = "Hello"\n\tprint self.extra'
exec(s)
self.yolo = types.MethodType(yolo, self)
what().yolo()
On a side note, why do you even need exec in this case? You can just as well write
import types
class what:
def __init__(self):
def yolo(self):
self.extra = "Hello"
print self.extra
self.yolo = types.MethodType(yolo, self)
what().yolo()
Edit: for the sake of completeness, one might prefer a solution through the descriptor protocol:
class what:
def __init__(self):
def yolo(self):
self.extra = "Hello"
print self.extra
self.yolo = yolo.__get__(self)
what().yolo()
Another way, seems more elegant to me:
class what:
pass
ld = {}
exec("""
def yolo(self):
self.extra = "Hello"
print(self.extra)
""", None, ld)
# print('locals got: {}'.format(ld))
for name, value in ld.items():
setattr(what, name, value)
what().yolo()

Categories