How to properly extend other classes in Python? (python v3.3) - python

I've started to learn python in the past few days, and while exploring object-oriented programming I'm running into problems.
I'm using Eclipse while running the pydev plugin, am running on the python 3.3 beta, and am using a windows 64 bit system.
I can initialize a class fine and use any methods within it, as long as I'm not trying to extend the superclass (each class I've coded in a different source file)
For example, the following code compiles and runs fine.
class pythonSuper:
string1 = "hello"
def printS():
print pythonSuper.string1
and the code to access and run it...
from stackoverflow.questions import pythonSuper
class pythonSub:
pysuper = pythonSuper.pythonSuper()
pysuper.printS()
Like I said, that works. The following code doesn't
class pythonSuper: """Same superclass as above. unmodified, except for the spacing"""
string1 = "hello"
def printS(self):
print(pythonSuper.string1)
Well, that's not quite true. The superclass is absolutely fine, at least to my knowledge. It's the subclass that weirds out
from stackoverflow.questions import pythonSuper
class pythonSub(pythonSuper):
pass
pythonObject = pythonSub()
pythonSub.pythonSuper.printS()
when the subclass is run Eclipse prints out this error
Traceback (most recent call last):
File "C:\Users\Anish\workspace\Python 3.3\stackoverflow\questions\pythonSub.py",
line 7, in <module>
class pythonSub(pythonSuper):
TypeError: module.__init__() takes at most 2 arguments (3 given)
I have no idea what's going on. I've been learning python from thenewboston's tutorials, but those are outdated (I think his tutorial code uses python version 2.7). He also codes in IDLE, which means that his classes are all contained in one file. Mine, however, are all coded in files of their own. That means I have no idea whether the code errors I'm getting are the result of outdated syntax or my lack of knowledge on this language. But I digress. If anyone could post back with a solution and/or explanation of why the code is going wrong and what I could do to fix it. An explanation would be preferred. I'd rather know what I'm doing wrong so I can avoid and fix the problem in similar situations than just copy and paste some code and see that it works.
Thanks, and I look forward to your answers

I ran your code, albeit with a few modifications and it runs perfectly. Here is my code:
pythonSuper:
class pythonSuper:
string1 = 'hello'
def printS(self):
print(self.string1)
main:
from pythonSuper import pythonSuper as pySuper
class pythonSub(pySuper):
pass
pythonObject = pythonSub()
pythonObject.printS()
NOTE: The change I have made to your code is the following:
In your code, you have written pythonSub.pythonSuper.printS() which is not correct, because via pythonSub you already support a printS() method, directly inherited from the superclass. So there is no need to refer to the superclass explicitly in that statement. The statement that I used to substitute the aforementioned one, pythonObject.printS(), seems to have addressed this issue.

pythonSuper refers to the module, not the class.
class pythonSub(pythonSuper.pythonSuper):
pass

Related

Linting classes created at runtime in Python

For context, I am using the Python ctypes library to interface with a C library. It isn't necessary to be familiar with C or ctypes to answer this question however. All of this is taking place in the context of a python module I am creating.
In short, my question is: how can I allow Python linters (e.g. PyCharm or plugin for neovim) to lint objects that are created at runtime? "You can't" is not an answer ;). Of course there is always a way, with scripting and the like. I want to know what I would be looking at for the easiest way.
First I introduce my problem and the current approach I am taking. Second, I will describe what I want to do, and ask how.
Within this C library, a whole bunch of error codes are defined. I translated this information from the .h header file into a Python enum:
# CustomErrors.py
from enum import Enum
class CustomErrors(Enum):
ERROR_BROKEN = 1
ERROR_KAPUTT = 2
ERROR_BORKED = 3
Initially, my approach is to have a single exception class containing a type field which described the specific error:
# CustomException.py
from CustomErrors import CustomErrors
class CustomException(Exception):
def __init__(self, customErr):
assert type(customErr) is CustomError
self.type = customErr
super().__init__()
Then, as needed I can raise CustomException(CustomErrors.ERROR_KAPUTT).
Now, what I want to do is create a separate exception class corresponding to each of the enum items in CustomErrors. I believe it is possible to create types at runtime with MyException = type('MyException', (Exception,), {'__doc__' : 'Docstring for ABC class.'}).
I can create the exception classes at runtime like so:
#CustomException.py
from CustomErrors import CustomErrors
...
for ce in CustomErrors:
n = ce.name
vars()[n] = type(n, (Exception,), {'__doc__' : 'Docstring for {0:s} class.'.format(n)})
Note: the reason I want to create these at runtime is to avoid hard-coding of an Exception list that change in the future. I already have the problem of extracting the C enum automatically on the backburner.
This is all well and good, but I have a problem: static analysis cannot resolve the names of these exceptions defined in CustomException. This means PyCharm and other editors for Python will not be able to automatically resolve the names of the exceptions as a suggested autocomplete list when the user types CustomException.. This is not acceptable, as this is code for the end user, who will need to access the exception names for use in try-except constructs.
Here is the only solution I have been able to think of: writing a script which generates the .py files containing the exception names. I can do this using bash. Maybe people will tell me this is really the only option. But I would like to know what other approaches are suggested for solving this problem. Thanks for reading.
You can add a comment to tell mypy to ignore dynamically defined attribute errors. Perhaps the linters that you use share a similar way to silence such errors.
mypy docs on silencing errors based on error codes
This example shows how to ignore an error about an imported name mypy thinks is undefined:
# 'foo' is defined in 'foolib', even though mypy can't see the
# definition.
from foolib import foo # type: ignore[attr-defined]

Pylint complains about method 'data_received' not overridden, for RequestHandler

For example:
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('data.html', items = [])
It yields the following Pylint error:
warning (W0223, abstract-method, MainHandler) Method 'data_received' is abstract in class 'RequestHandler' but is not overridden
I understand that somehow it wants me to override this data_received method, but I do not understand why, and what it is for?
This is actually a problem with Pylint that's sort of unavoidable with the nature of Python.
The RequestHandler class has a lot of methods that act as hooks you can override in order to do different things, but only some of those hooks may actually be called, depending on your application's code. To make sure you're implementing everything you're supposed to when you're using certain functionality, the default data_received implementation throws a NotImplementedError that will get triggered when you do something that expects your class to have a custom implementation.
Normally this isn't any kind of issue because Python lets you have code paths that fail and doesn't throw any errors. Because Pylint tries to "help" make sure you've done everything you're supposed to, it's seeing that NotImplementedError throw and is warning you that you could trigger it depending on what you do.
The real problem is that because Python is an interpreted language, it's hard for a tool like Pylint to look at your code and make sure it's "safe". Python gives you a lot of flexibility and power, but in turn you bear the burden of keeping your program's logic straight in your head and knowing what possible problems are actually problems, and what aren't.
Luckily, Pylint is aware of its own limitations and gives you nice tools to disable extraneous warnings. Add the comment line
# pylint: disable=W0223
right before your class definition and the warning should stop popping up for this instance while leaving everything else alone.
I am running into the same issue as the OP, except my PyCharm (2018.3.4) seems not to be using Pylint, but its own inspection engine. I managed to solve a similar issue with the similar trick as R Phillip Castagna suggested:
# noinspection PyAbstractClass
class XyzRequestHandler(tornado.web.RequestHandler):
def prepare(self):
print('-' * 100)
self.set_header('Access-Control-Allow-Origin', '*')
def print_n_respond(self, result):
response = json.dumps(result)
print('responding with:\n', response)
print()
self.write(response)
Here is a list of PyCharm's inspections.

Python class can't be updated after being compiled

I just started with python a couple of days ago, coming from a C++ background. When I write a class, call it by a script, and afterwards update the interface of the class, I get some behaviour I find very unintuitive.
Once successfully compiled, the class seems to be not changeable anymore. Here an example:
testModule.py:
class testClass:
def __init__(self,_A):
self.First=_A
def Method(self, X, Y):
print X
testScript.py:
import testModule
tm=testModuleB.testClass(10)
tm.Method(3, 4)
Execution gives me
3
Now I change the argument list of Method:
def Method(self, X):
, I delete the testModule.pyc and in my script I call
tm.Method(3)
As result, I get
TypeError: Method() takes exactly 3 arguments (2 given)
What am I doing wrong? Why does the script not use the updated version of the class? I use the Canopy editor but I saw this behaviour also with the python.exe interpreter.
And apologies, if something similar was asked before. I did not find a question related to this one.
Python loads the code objects into memory; the class statement is executed when a file is first imported an a class object is created and stored in the module namespace. Subsequent imports re-use the already created objects.
The .pyc file is only used the next time the module is imported for the first time that Python session. Replacing the file will not result in a module reload.
You can use the reload() function to force Python to replace an already-loaded module with fresh code from disk. Note that any and all other direct references to a class are not replaced; an instance of the testClass class (tm in your case) would still reference the old class object.
When developing code, it is often just easier to restart the Python interpreter and start afresh. That way you don't have to worry about hunting down all direct references and replacing those, for example.
testModule is already loaded in your interpreter. Deleting the pyc file won't change anything. You will need to do reload(testModule), or even better restart the interpreter.
Deleting the .pyc file cannot do the change in your case. When you import a module for the first time on the interpreter, it gets completely loaded on the interpreter and deleting the files or modifying won't change anything.
Better restart the interpreter or use the built-in reload function.

Subclassing file class in Python raises NameError

I have to do a very simple project in python where I add error checking to the built in file class. So far, I've got:
class RobustFile(file):
def __init__(self,name,mode):
file.__init__(self,name,mode)
I'm just starting out, but to make sure I hadn't messed anything up, I ran it. Well, right off the bat, I raised a NameError because it didn't recognize file. I tried tweaking it, I looked it up on the internet, I copied examples using the same format, and...all NameError. Can anyone shed some light on how exactly to subclass file?
You're probably using Python 3, which no longer has a file type.
Instead, as noted in the Python 3 documentation's I/O Overview, it has a number of different stream types that are all derived from one of _io.TextIOBase, _io.BufferedIOBase, or _io.RawIOBase, which are themselves derived from _io.IOBase.
Works fine in python 2.6.6:
In [44]: class RobustFile(file):
def __init__(self,name,mode):
file.__init__(self,name,mode)
....:
In [47]: fp = RobustFile('foo','w')
In [48]: fp.writelines('bar')
In [49]: fp.close()

Problem using super(python 2.5.2)

I'm writing a plugin system for my program and I can't get past one thing:
class ThingLoader(object):
'''
Loader class
'''
def loadPlugins(self):
'''
Get all the plugins from plugins folder
'''
from diones.thingpad.plugin.IntrospectionHelper import loadClasses
classList=loadClasses('./plugins', IPlugin)#Gets a list of
#plugin classes
self.plugins={}#Dictionary that should be filled with
#touples of objects and theirs states, activated, deactivated.
classList[0](self)#Runs nicelly
foo = classList[1]
print foo#prints <class 'TestPlugin.TestPlugin'>
foo(self)#Raise an exception
The test plugin looks like this:
import diones.thingpad.plugin.IPlugin as plugin
class TestPlugin(plugin.IPlugin):
'''
classdocs
'''
def __init__(self, loader):
self.name='Test Plugin'
super(TestPlugin, self).__init__(loader)
Now the IPlugin looks like this:
class IPlugin(object):
'''
classdocs
'''
name=''
def __init__(self, loader):
self.loader=loader
def activate(self):
pass
All the IPlugin classes works flawlessy by them selves, but when called by ThingLoader the program gets an exception:
File "./plugins\TestPlugin.py", line 13, in __init__
super(TestPlugin, self).__init__(loader) NameError:
global name 'super' is not defined
I looked all around and I simply don't know what is going on.
‘super’ is a builtin. Unless you went out of your way to delete builtins, you shouldn't ever see “global name 'super' is not defined”.
I'm looking at your user web link where there is a dump of IntrospectionHelper. It's very hard to read without the indentation, but it looks like you may be doing exactly that:
built_in_list = ['__builtins__', '__doc__', '__file__', '__name__']
for i in built_in_list:
if i in module.__dict__:
del module.__dict__[i]
That's the original module dict you're changing there, not an informational copy you are about to return! Delete these members from a live module and you can expect much more than ‘super’ to break.
It's very hard to keep track of what that module is doing, but my reaction is there is far too much magic in it. The average Python program should never need to be messing around with the import system, sys.path, and monkey-patching __magic__ module members. A little bit of magic can be a neat trick, but this is extremely fragile. Just off the top of my head from browsing it, the code could be broken by things like:
name clashes with top-level modules
any use of new-style classes
modules supplied only as compiled bytecode
zipimporter
From the incredibly round-about functions like getClassDefinitions, extractModuleNames and isFromBase, it looks to me like you still have quite a bit to learn about the basics of how Python works. (Clues: getattr, module.__name__ and issubclass, respectively.)
In this case now is not the time to be diving into import magic! It's hard. Instead, do things The Normal Python Way. It may be a little more typing to say at the bottom of a package's mypackage/__init__.py:
from mypackage import fooplugin, barplugin, bazplugin
plugins= [fooplugin.FooPlugin, barplugin.BarPlugin, bazplugin.BazPlugin]
but it'll work and be understood everywhere without relying on a nest of complex, fragile magic.
Incidentally, unless you are planning on some in-depth multiple inheritance work (and again, now may not be the time for that), you probably don't even need to use super(). The usual “IPlugin.__init__(self, ...)” method of calling a known superclass is the straightforward thing to do; super() is not always “the newer, better way of doing things” and there are things you should understand about it before you go charging into using it.
Unless you're running a version of Python earlier than 2.2 (pretty unlikely), super() is definitely a built-in function (available in every scope, and without importing anything).
May be worth checking your version of Python (just start up the interactive prompt by typing python at the command line).

Categories