Python - Proper way of inheritance, super and initialize object (selenium, phantomjs) - python

I wrote pretty simple code, but I want to know is it proper way to resolve problem:
from selenium import webdriver
class MyClass(webdriver.PhantomJS):
def __init__(self, *args, **kwargs):
phantomjs_path = 'node_modules/.bin/phantomjs'
self.driver = webdriver.PhantomJS(phantomjs_path)
super().__init__(phantomjs_path, *args, **kwargs)
I created class, which inherit from selenium.webdriver.PhantomJS - of course I execute super(). Additionaly I create object self.driver.
Can/Should I combine last two lines in one?

You'd not use the second-last line at all. You are creating another instance inside your subclass there. self.driver is basically the same thing as self now, only without your extra methods.
Omit it altogether:
class MyClass(webdriver.PhantomJS):
def __init__(self, *args, **kwargs):
phantomjs_path = 'node_modules/.bin/phantomjs'
super().__init__(phantomjs_path, *args, **kwargs)
In your method, self is the instance already.

Related

How can I most beautifully apply the methods of a parent class to all (registered) instances?

For ease of programming, I am looking to implement something like this:
SpecialCaseChildClass.apply_to_all_members_of_child_class.any_method_of_parent()
Even more awesome would be an implementation in which I can supply a list of arguments that allows me to iterate arguments:
SpecialCaseChildClass.apply_to_all.any_method_of_Parent(
argument1_of_parent_method = range(number_of_children), argument2_of_parent_method = range(number_of_children))
So my suggestion in words: (How) can I put an apply_to_all_members_of_class object/method between child and parent methods in command chain?
I have tried to implement this functionality with a class method:
class SpecialCaseChildClass(Parent):
registry = []
def __init__(self, **kwargs, *args):
super().__init__(self, **kwargs, *args)
registry.append(self)
#classmethod
def apply_to_all(cls, any_function, *args, **kwargs):
[any_function(child, *args, **kwargs) for child in cls.registry]
which would allows the following:
SpecialCaseChildClass.apply_to_all_members_of_child_class(ParentClass.any_Parent_method, *args, *kwargs)
but that is not as pretty as simple addition to the chain.
Is such a thing possible relatively simply?
Is it even desirable?

decorator to generate new classes into namespace

The specific use case I need it for is to deprecate class names.
Suppose we have class A in an earlier version and we want to deprecate its name but keep backwards compatibility:
class A(B):
def __init__(self, *args, **kwargs):
warnings.warn('deprecation!')
super(A, self).__init__(*args, **kwargs)
... and B now has the correct implementation.
When we create a class A, we will run into a deprecation warning here. We can also use the deprecated module for decorators on __init__.
However, I want to skip this process and write less code, and hopefully achieve something like:
#deprecated_alias('A')
class B:
# ... do something
Can I somehow inject the classname into the module-level namespace so that I can use A like this?
Can I somehow inject the classname into the module-level namespace so that I can use A like this?
Yes. The class decorator should:
create a new type, with overridden __init__ method, using the 3-argument invocation of type
get the module of the original class, sys.modules[original_class.__module__]
bind the new class in the module namespace, using setattr
return the original class unchanged
Example:
import sys
def deprecated_alias(name):
def decorator(class_):
mod = sys.modules[class_.__module__]
if hasattr(mod, name):
raise Exception('uhoh, name collision')
NewClass = type(name, (class_,), {'__init__': ...})
setattr(mod, name, NewClass)
return class_
return decorator
#deprecated_alias('A')
class B:
pass
I don't recommend this approach - too much magic. It will confuse IDEs and break autocompletion.
A less magical approach, perhaps? This could also be made into a decorator, and use __subclasscheck__/__subclasshook__ if you need to control the finer details of inheritance.
class A(B):
def __init__(self, *args, **kwargs):
warnings.warn('deprecation!')
return B(*args, **kwargs)
While this is not exactly what you asked for, it is substantially less magical and ultimately the same number of lines of code. It is also far more explicit:
import warnings
def deprecated(DeprecatedByClass):
class Deprecated(DeprecatedByClass):
def __new__(cls, *args, **kwargs):
warnings.warn("deprecation!")
return super(Deprecated, cls).__new__(cls, *args, **kwargs)
return Deprecated
You can then use this like so:
class B:
pass
A = deprecated(B)

Inherit Variables from Parent Class

I am trying to work out how to inherit variables from a parent class.
I have two classes (simplified but same principle):
class Database(object):
def __init__(self, post, *args, **kwargs):
self.post = post
self.report()
def report(self):
#... obtain variables from post ...
self.database_id = self.post['id']
#... save data to database
class PDF(Database):
def __init__(self, post, *args, **kwargs):
Database.__init__(self, post, *args, **kwargs)
#... if i try to access self.database_id now, it returns an error ...
print(self.database_id)
instantiating script:
Database(request.POST)
PDF(request.POST)
I have tried just instantiating CreatePDF, as i thought the Database.__init__(self, post, *args, **kwargs) line would the Database class, but this does not work either.
I am trying to find the most pythonic way to do inherit. I can obviously obtain self.database_id from the post dict passed to PDF(), however I do not see the point in doing this twice, if I can use inheritance.
Thanks
Use:
class PDF(Database):
def __init__(self, post, *args, **kwargs):
# Stuff
super().__init__(post, *args, **kwargs)
The correct approach to instantiated an inherited class is to call super().init(args), which in this case calls Database.init because of method resolution order.
See http://amyboyle.ninja/Python-Inheritance

Define field in ndb using strings

This is my code:
class SocialNodeSubscription(model.Model):
def __init__(self, *args, **kwargs):
permissions=["post","read","reply","admin"]
for p in permissions:
self.__dict__["can_"+p]=model.BooleanProperty(default=True)
I need to dynamically define fields in my model but this doesn't seem to work because dict is not the right place where to put my fields.
For who don't know about ndb, this is how it would look like going the easier way.
class SocialNodeSubscription(model.Model):
def __init__(self, *args, **kwargs):
self.can_write=model.BooleanProperty(default=True)
self.can_read=model.BooleanProperty(default=True)
...
Edit:
Now my code looks like this:
def __init__(self, *args, **kwargs):
permissions=["post","read","reply","admin"]
for p in permissions:
self._properties["can_"+p]=model.BooleanProperty(default=True)
self._fix_up_properties()
But still i get this error.
File "C:\Program Files
(x86)\Google\google_appengine\google\appengine\ext\ndb\model.py", line
972, in _store_value
entity._values[self._name] = value TypeError: 'NoneType' object does not support item assignment
What does it mean?
It's _properties,
just have a look at its metaclass MetaModel and class method _fix_up_properties.
Definition of _properties:
# Class variables updated by _fix_up_properties()
_properties = None
Method:
#classmethod
def _fix_up_properties(cls):
"""Fix up the properties by calling their _fix_up() method.
Note: This is called by MetaModel, but may also be called manually
after dynamically updating a model class.
"""
Use an expando model for a model with dynamic properties.

Deriving a class from TestCase throws two errors

I have some basic setup/teardown code that I want to reuse in a whole bunch of unit tests. So I got the bright idea of creating some derived classes to avoid repeating code in every test class.
In so doing, I received two strange errors. One, I cannot solve. Here is the unsolvable one:
AttributeError: 'TestDesktopRootController' object has no attribute '_testMethodName'
Here is my base class:
import unittest
import twill
import cherrypy
from cherrypy._cpwsgi import CPWSGIApp
class BaseControllerTest(unittest.TestCase):
def __init__(self):
self.controller = None
def setUp(self):
app = cherrypy.Application(self.controller)
wsgi = CPWSGIApp(app)
twill.add_wsgi_intercept('localhost', 8080, lambda : wsgi)
def tearDown(self):
twill.remove_wsgi_intercept('localhost', 8080)
And here is my derived class:
import twill
from base_controller_test import BaseControllerTest
class TestMyController(BaseControllerTest):
def __init__(self, args):
self.controller = MyController()
BaseControllerTest.__init__(self)
def test_root(self):
script = "find 'Contacts'"
twill.execute_string(script, initial_url='http://localhost:8080/')
The other strange error is:
TypeError: __init__() takes exactly 1 argument (2 given)
The "solution" to that was to add the word "args" to my __init__ function in the derived class. Is there any way to avoid that?
Remember, I have two errors in this one.
It's because you're overriding __init__() incorrectly. Almost certainly, you don't want to override __init__() at all; you should do everything in setUp(). I've been using unittest for >10 years and I don't think I've ever overridden __init__().
However, if you really do need to override __init__(), remember that you don't control where your constructor is called -- the framework calls it for you. So you have to provide a signature that it can call. From the source code (unittest/case.py), that signature is:
def __init__(self, methodName='runTest'):
The safe way to do this is to accept any arguments and just pass 'em up to the base class. Here is a working implementation:
class BaseTest(unittest.TestCase):
def __init__(self, *args, **kwargs):
unittest.TestCase.__init__(self, *args, **kwargs)
def setUp(self):
print "Base.setUp()"
def tearDown(self):
print "Base.tearDown()"
class TestSomething(BaseTest):
def __init__(self, *args, **kwargs):
BaseTest.__init__(self, *args, **kwargs)
self.controller = object()
def test_silly(self):
self.assertTrue(1+1 == 2)
In BaseController's __init__ you need to call unittest.TestCase's __init__ just like you did in TestMyController.
The call to construct a TestCase from the framework may be passing an argument. The best way to handle this for deriving classes is:
class my_subclass(parentclass):
def __init__(self, *args, **kw):
parentclass.__init__(self, *args, **kw)
...

Categories