Using a method from the parent class - python

I want to call a method from the parent class in a child class.
I use XX.__init__() in my child class and call the press function from the parent class. But it fails when I run the following code:
Func.py
class PC:
def __init__(self):
PCKeyDis = {}
self.PCKeyDis = PCKeyDis
def Press(self,key):
KeyDis = self.PCKeyDis
if len(key)==1 and key.islower():
key = key.upper()
win32api.keybd_event(KeyDis[key],0,0,0)
time.sleep(0.1)
win32api.keybd_event(KeyDis[key],0,win32con.KEYEVENTF_KEYUP,0)
class PCFunc(PC):
def __init__(self):
pass
def Sentence(self,string):
PC.__init__()
strlist = list(string)
for i in xrange(len(strlist)):
if strlist[i] == ' ':
strlist[i] = 'Space'
PC.Press(strlist[i]) #use this function
action.py
import Func
import win32gui
PC = Func.PC()
PCFunc = Func.PCFunc ()
win32gui.SetForegroundWindow(win32gui.FindWindow(winclass,winnm))
PCFunc.Sentence(path)
I get:
unbound method Sentence() must be called with PCFunc instance as first argument (got str instance instead)

If you want to call the constructor of the base class, then you do it on instantiation in the __init__() method, not in the Sentence() method:
def __init__(self):
super(self.__class__, self).__init__()
Since Sentence() is an instance method, you need to call it via an instance of the class (like the error tells you):
pc_func = PCFunc()
pc_func.Sentence(var)
Here you are calling the method with an undefined variable:
PCFunc.Sentence(path)
Instead you need to give a string as parameter, so either write Sentence('path'), or define the variable first:
path = 'my path'
pc_func.Sentence(path)
Do not use the same name as the class name for an instance of the class:
PCFunc = Func.PCFunc ()
Otherwise the variable name storing the instance overwrites the class name.
Apart from that, it is unclear what your code is actually supposed to do. Have a look at the Python code conventions for a first step to making your code more readible. Then do some research about classes and inheritance.

The code you posted does not produce the error you posted. Here is an example that will produce that error:
class Dog:
def do_stuff(self, string):
print string
d = Dog()
d.do_stuff('hello')
Dog.do_stuff(d, 'goodbye')
Dog.do_stuff('goodbye')
--output:--
hello
goodbye
Traceback (most recent call last):
File "1.py", line 9, in <module>
Dog.do_stuff('goodbye')
TypeError: unbound method do_stuff() must be called with Dog instance as first argument (got str instance instead)
An __init__() function can also produce that error:
class Dog:
def __init__(self):
pass
def do_stuff(self, string):
print(string)
Dog.__init__()
--output:--
Traceback (most recent call last):
File "1.py", line 7, in <module>
Dog.__init__()
TypeError: unbound method __init__() must be called with Dog instance as first argument (got nothing instead)
In the line:
d.do_stuff('hello')
the fragment d.do_stuff causes python to create and return a bound method object--which is then immediately executed by the function execution operator () in the fragment ('hello’). The bound method is bound to the instance d, hence the reason it is called a bound method. A bound method automatically passes the instance it contains to the method when the method is executed.
On the other hand, when you write:
Dog.do_stuff(....)
the fragment Dog.do_stuff causes python to create and return an unbound method. An unbound method does not contain an instance, so when an unbound method is executed by the function execution operator (), you must manually pass an instance. (In python3, things changed and you can pass anything as the first argument--an instance of the class isn't required.)

Related

How to define a custom function of an instance using setattr

I'm trying to add a method to a class dynamically, but I keep running into an error where self is not passed to a the new function. For instance:
class Dummy():
def say_hi(self):
print("hi")
def new_method(self):
print("bye")
dummy = Dummy()
setattr(dummy, "say_bye", new_method)
dummy.say_bye()
results in the following error:
Traceback (most recent call last):
File "main.py", line 13, in <module>
dummy.say_bye()
TypeError: new_method() missing 1 required positional argument: 'self'
What am I doing wrong?
Use types.MethodType feature:
from types import MethodType
class Dummy():
def say_hi(self):
print("hi")
def new_method(self):
print("bye")
dummy = Dummy()
dummy.say_bye = MethodType(new_method, dummy)
dummy.say_bye() # bye
You are setting the function new_method as an attribute of the dummy object.
If you do print(dummy.__dict__) you'll see something like this:
{'say_bye': <function new_method at 0x7fcd949af668>}
This means that your dummy object has the function new_method as an attribute, so when you do dummy.say_bye(), you're calling the function you have as an attribute without any argument.
It is not a function of the Dummy class, it is just a function that your dummy object has as an attribute.
You can achieve the functionality you are looking for using RomanPerekhrest's answer.
Hope it helps.
Cheers!

super() in a decorated subclass in Python 2

I'm trying to use super in a subclass which is wrapped in another class using a class decorator:
def class_decorator(cls):
class WrapperClass(object):
def make_instance(self):
return cls()
return WrapperClass
class MyClass(object):
def say(self, x):
print(x)
#class_decorator
class MySubclass(MyClass):
def say(self, x):
super(MySubclass, self).say(x.upper())
However, the call to super fails:
>>> MySubclass().make_instance().say('hello')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in say
TypeError: super(type, obj): obj must be an instance or subtype of type
The problem is that, when say is called, MySubclass doesn't refer to the original class anymore, but to the return value of the decorator.
One possible solution would be to store the value of MySubclass before decorating it:
class MySubclass(MyClass):
def say(self, x):
super(_MySubclass, self).say(x.upper())
_MySubclass = MySubclass
MySubclass = class_decorator(MySubclass)
This works, but isn't intuitive and would need to be repeated for each decorated subclass. I'm looking for a way that doesn't need additional boilerplate for each decorated subclass -- adding more code in one place (say, the decorator) would be OK.
Update: In Python 3 this isn't a problem, since you can use __class__ (or the super variant without arguments), so the following works:
#class_decorator
class MySubclass(MyClass):
def say(self, x):
super().say(x.upper())
Unfortunately, I'm stuck with Python 2.7 for this project.
The problem is that your decorator returns a different class than python (or anyone who uses your code) expects. super not working is just one of the many unfortunate consequences:
>>> isinstance(MySubclass().make_instance(), MySubclass)
False
>>> issubclass(MySubclass, MyClass)
False
>>> pickle.dumps(MySubclass().make_instance())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class '__main__.MySubclass'>: it's not the same object as __main__.MySubclass
This is why a class decorator should modify the class instead of returning a different one. The correct implementation would look like this:
def class_decorator(wrapped_cls):
#classmethod
def make_instance(cls):
return cls()
wrapped_cls.make_instance = make_instance
return wrapped_cls
Now super and everything else will work as expected:
>>> MySubclass().make_instance().say('hello')
HELLO
The problem occurs because at the time when MySubclass.say() is called, the global symbol MySubclass no longer refers to what's defined in your code as 'class MySubclass'. It is an instance of WrapperClass, which isn't in any way related to MySubclass.
If you are using Python3, you can get around this by NOT passing any arguments to 'super', like this:
super().say(x.upper())
I don't really know why you use the specific construct that you have, but it does look strange that a sub-class of MyClass that defines 'say()' - and has itself a 'say()' method in the source code would have to end up as something that does not have that method - which is the case in your code.
Note you could change the class WrapperClass line to make it read
class WrapperClass(cls):
this will make your wrapper a sub-class of the one you just decorated. This doesn't help with your super(SubClass, self) call - you still need to remove the args (which is OK only on Python3), but at least an instance created as x=MySubclass() would have a 'say' method, as one would expect at first glance.
EDIT: I've come up with a way around this, but it really looks odd and has the disadvantage of making the 'wrapped' class know that it is being wrapped (and it becomes reliant on that, making it unusable if you remove the decorator):
def class_decorator(cls):
class WrapperClass(object):
def make_instance(self):
i = cls()
i._wrapped = cls
return i
return WrapperClass
class MyClass(object):
def say(self, x):
print(x)
#class_decorator
class MySubclass(MyClass):
def say(self, x):
super(self._wrapped, self).say(x.upper())
# make_instance returns inst of the original class, non-decorated i = MySubclass().make_instance() i.say('hello')
In essence, _wrapped saves a class reference as it was at declaration time, consistent with using the regular super(this_class_name, self) builtin call.

How to run python class method at command line

I'm trying to connect to a sqllite database using:
class Sqllite_utilities(object):
def __init__(self):
parser = argparse.ArgumentParser()
parser.add_argument("-s","--source", type=str,
help="source table from db")
args = parser.parse_args()
print(args)
source_table= args.source
db_path = "sqlite:///"+settings.SETTINGS_PATH+"\\data.db"
dataset_db = dataset.connect(db_path)
self.dataset_table = dataset_db[source_table]
def missing_ids(self):
for user in self.dataset_table:
print(user['id'])
if __name__ == '__main__':
Sqllite_utilities.missing_ids(sys.argv[1])
When I do:
$ python find_missing_records.py -s test
I get:
Traceback (most recent call last):
File "find_missing_records.py", line 41, in <module>
Sqllite_utilities.missing_ids(sys.argv[1])
TypeError: unbound method missing_ids() must be called with Sqllite_utilities instance as first argument (got str instance instead)
(contact2E)
What am I doing wrong?
When doing:
Sqllite_utilities.missing_ids(sys.argv[1])
you're calling the missing_ids instance method with sys.argv[1] (a string) as self (functional way to call instance methods, but with the wrong object), which explains the error message.
It's clear that you have to create an instance of your object before being able to use the instance methods (the ones without #staticmethod or #classmethod decorators):
if __name__ == '__main__':
s = Sqllite_utilities()
the constructor parses the arguments, using argparse.ArgumentParser which uses sys.argv by default, so leave that aside, it'll work.
Then call your method on the instance:
s.missing_ids()
s is implicitly passed as self when calling missing_ids
As suggested from the top answer to this question: unbound method f() must be called with fibo_ instance as first argument (got classobj instance instead)
Add to your code:
if __name__ == '__main__':
obj = Sqllite_utilities()
s.missing_ids()

Importing class-type global variables

I'm trying to implement a configuration system contained within a module. The core configuration variable is a class instance and a global variable in this module. It seems that when I import this variable, I cannot use it as a class for some reason.
Consider this minimal example:
foomodule.py:
class FooClass:
number = 5
def bar (self):
return self.number
foo = FooClass
foo.number = 5
main.py
from foomodule import foo
print foo.bar()
Running main.py results in a cryptic error message:
Traceback (most recent call last):
File "main.py", line 2, in <module>
print foo.bar()
TypeError: unbound method bar() must be called with FooClass instance as first argument (got nothing instead)
But I am calling it with a FooClass instance which I'd think should be the self argument like it usually is. What am I doing wrong here?
You only bound foo to the class; you didn't make it an instance:
foo = FooClass # only creates an additional reference
Call the class:
foo = FooClass() # creates an instance of FooClass
In Python you usually don't use accessor methods; just reference foo.number in your main module, rather than use foo.bar() to obtain it.
In your example foo is just an alias for FooClass. I assume that your actual problem is more complicated than your snippet. However, if you really need a class method, you can annotate it with #classmethod decorator.
class FooClass(object):
number = 5
#classmethod
def bar(cls):
return cls.number
To use your the class you could do:
from foomodule import Foo
Foo.bar()
Or you can access the class member directly
Foo.number

Do python subclasses have to necessarily call their superclass constructor?

I have this:
#!/usr/bin/env python
class myclass1(object):
def __init__(self, arg1):
self.var1 = arg1
class myclass2(myclass1):
def f1(self):
print "in f1"
class myclass3(myclass1):
def __init__(self, arg1):
self.var2 = arg1
self.c2 = myclass2()
p= myclass3(5)
This gives me an error:
Traceback (most recent call last):
File "./pythoninherit.py", line 39, in <module>
p= myclass3(5)
File "./pythoninherit.py", line 29, in __init__
self.c2 = myclass2()
TypeError: __init__() takes exactly 2 arguments (1 given)
Question:
Why is the error given?
Why was the myclass1 __init__ called automatically in this case?
I was under the impression that this does not happen in python.
The __init__ method in myclass1 is inherited by myclass2. When you instantiate myclass2, you're calling the __init__ method from myclass1, but it takes an argument and you're not passing it.
They don't always have to, but they have to override the base __init__ if they're going to change how initialization works. If the base class requires an argument to initialize, and the subclass doesn't, then the subclass is changing the API and needs to write its own __init__. (Whether it calls the base class depends on whether it wants to inherit the base init behavior.)
In your example, myclass2 inherits the __init__ from myclass1, but you don't pass an argument when you do myclass2(), so it doesn't work. If you want to be able to instantiate myclass2 without passing an argument, you need to write its __init__ to allow that.
The problem is that myclass2 is a subclass on myclass1 and you aren't passing arg1 to myclass2 on
self.c2 = myclass2()
needs to be
self.c2 = myclass2(arg1)
So the myclass2 is calling myclass1's __init__ which is expecting one argument
like you gave 5 as a parameter for myclass3, you must give something for myclass2 inside myclass3, like:
class myclass3(myclass1):
def __init__(self, arg1):
self.var2 = arg1
self.c2 = myclass2(5)

Categories

Resources