I'm having an issue where a class variable is set correctly and displayed correctly inside __init__() when using print, however, trying to display that same variable inside the __str__ method displays None. Three other variables generated the exact same way do not exhibit this behavior.
Here is my code:
class RestRequest(urllib2.Request):
def __init__(self, host, path, data=""):
self.host = host
self.data = data
self.path = path
print "RestRequest::init() path: %s" % self.path
print "RestRequest::init() data: %s" % self.data
print "RestRequest::init() host: %s" % self.host
urllib2.Request.__init__(self, self.path)
def __str__(self):
return "RestRequest::__str__() host='%s', path='%s', data='%s'" % (self.host, self.path, self.data)
The print statements inside __init__() display the correct values. The value of host inside __str__ is None. The path, method and data variables display correctly inside __str__.
Any ideas?
You're running into a naming clash with attributes on the super class. You'll need to change the names of your attributes -- or set them after urllib2.Request.__init__ (assuming your values are compatible with what the superclass wants).
urllib2.Request sets self.host = None in __init__. To prevent unexpected things happening, call the superclass __init__ earlier in the subclass __init__:
class RestRequest(urllib2.Request):
def __init__(self, host, path, data=""):
urllib2.Request.__init__(self, path) # sets self.host = None
self.host = host # resets to correct value
self.path = path
self.data = data
Related
How do I re-initialize the base class using self.__init__?
In the following example my goal is to inherit eComm which is a socket derived driver. This handles connect/disconnect. If it disconnects we need to reinitialize it using __init__, however it appears to refer to the comDriver when self.__init__ is called.
How do I properly initialize the superclass to allow for this?
(else: self.__init__ in connect() is referring to comDriver, not eComm like it should)
The following is a simple case to reproduce the error in Python 3.x
class eComm():
def __init__(self):
self.s = "example object"
self.initialized = True
self.connected = False
def connect(self, IP_ADDRESS, PORT):
if self.initialized:
print(IP_ADDRESS, PORT)
else:
print("REINITIALIZING")
self.__init__()
self.connected = True
return(True)
class comDriver(eComm):
def __init__(self, IP_ADDRESS, PORT):
self.IP = IP_ADDRESS
self.PORT = PORT
super().__init__()
pass
def getTemp(self):
print("EXAMPLE FUNCTION")
return(1)
x = comDriver("192", 7)
x.connect("161", 6)
x.initialized = False
x.connect("111", 5)
IMO you're using the special method __init__ wrongly. It's meant to initialize a Python object, not anything outside that scope.
With your intention, I recommend that you create a separate initializer function, and call it from __init__. Here's an example:
class eComm():
def __init__(self):
self.initialize_eComm()
def initialize_eComm(self):
self.s = "example object"
self.initialized = True
self.connected = False
And then you can replace self.__init__() with self.initialize_eComm() to avoid name conflict in subclasses.
self.__init__ in connect() is referring to comDriver, not eComm like it should
This doesn't quite hold -- self refers to the calling object, which is comDriver. If you want to call to the __init__ method on eComm regardless of what classes extend it, you will have to reference in explicitly.
eComm.__init__(self)
But, the other answers and comments are right that this is not a good use of __init__.
I am trying to define a handler class for a socketserver. When the handler class had no __init__() method defined, my server worked. The message sent by the client was written to the output window. However, when I added an __init__() method to declare a class member, my program threw an exception because RequestHandlerClass required exactly one argument, and I was passing four arguments to it. After pounding my head into a brick wall for a while, I remembered that the BaseRequestHandler class has an override-able setup() method. I declared an override for it and declared my class member inside it, and it worked.
While I have a solution to my immediate problem, I'd like to understand this. Should I never declare my own __init__() method in a request handler class? Or if I should, how should it be declared?
Here's my code:
import socketserver
import logging
import logging.config
import json
from TWMSMessageHandler import TWMSMessageHandler
class SingleTCPHandler(socketserver.BaseRequestHandler):
# def __init__(self): ## causes an error
def setup(self):
self.messageHandler = TWMSMessageHandler()
# One instance per connection. Override handle(self) to customize action.
def handle(self):
# self.request is the client connection
data = self.request.recv(1024) # clip input at 1Kb
dataString = data.decode()
print ("Received data: " + dataString)
self.request.close()
class MyTCPServer(socketserver.TCPServer):
def __init__(self, serverAddress, handler):
super().__init__(serverAddress, handler)
def handle_timeout(self):
print ("No message received in {0} seconds".format(self.timeout))
if __name__ == "__main__":
with open('TWMSHandler_log_config.json', 'rt') as f:
config = json.load(f)
logging.config.dictConfig(config)
tcpServer = MyTCPServer(("127.0.0.1", 5006), SingleTCPHandler)
tcpServer.timeout = 30
loopCount = 0
while loopCount < 5:
try:
print ("About to wait for request")
tcpServer.handle_request()
print ("Back from handle_request")
loopCount = loopCount + 1
except Exception as Value:
print ("Oops! " + str(Value))
break
I'm assuming python 2.7 since you haven't specified otherwise, this should apply to python 3.x too, however.
If you take a look at the source code (https://hg.python.org/cpython/file/2.7/Lib/SocketServer.py#l631), the BaseRequestHandler class which you are overriding takes 3 arguments besides self: request, client_address, server. If you want to override __init__ you must be compatible with this signature, unless you also override the callsite that calls __init__ from within the TCPServer inheritance chain (You don't want to do this).
Since all that function does is to save state you would otherwise have to save yourself (Or call the base function through a super call), you may as well just use setup as you are.
I have 3 classes defined this way:
class Device:
Some method
class SSH:
def connect(self,type):
# code
def execute(self,cmd):
# code
class Netconf:
def connect(self,type):
# code
def execute(self,cmd):
# code
Note SSH and Netconf classes have same method names but they do things differently.
I have an instance of class Device and would like to access methods like this-
d = Device()
d.connect('cli') # <-- This should call SSH method and subsequently
# d.execute(cmd) should call execute method from SSH class
# too.
d.connect('netconf') # <-- This should call Netconf method and subsequently
# d.execute(cmd) should call execute method from
# Netconf class too.
The question is - how do I make it happen? I want to be able to use methods of SSH/Netconf class on Device class instance 'd' based on the input.
You can do this by storing the type of device connected in a private Device attribute and then forwarding most method calls to it by adding a custom __getattr__() method. This is a little tricky in the connect() method because that's were the target device is defined (as opposed to in the Device.__init__() initializer).
I also changed the variable you had named type to kind to avoid colliding with the built-in module of the same name.
class Device(object):
def connect(self, kind):
if kind == 'cli':
target = self._target = SSH()
elif kind == 'netconf':
target = self._target = Netconf()
else:
raise ValueError('Unknown device {!r}'.format(kind))
return target.connect(kind)
def __getattr__(self, name):
return getattr(self._target, name)
class SSH(object):
def connect(self, kind):
print('SSH.connect called with kind {!r}'.format(kind))
def execute(self, cmd):
print('SSH.execute called with cmd {!r}'.format(cmd))
class Netconf(object):
def connect(self, kind):
print('Netconf.connect called with kind {!r}'.format(kind))
def execute(self, cmd):
print('Netconf.execute called with cmd {!r}'.format(cmd))
d = Device()
d.connect('cli')
d.execute('cmd1')
d.connect('netconf')
d.execute('cmd2')
Output:
SSH.connect called with kind 'cli'
SSH.execute called with cmd 'cmd1'
Netconf.connect called with kind 'netconf'
Netconf.execute called with cmd 'cmd2'
You should implement the Strategy Pattern. The connect() method should instantiate the appropriate class (detach()ing from the previous if required) and store it, and then other methods should delegate to the stored object.
I'm learning how to program and handle Python classes and have found this problem.
I have this files (example.py) and inside this file I'm importing two classes.
example.py
..
from class_folder.automobile import Class_Automobile
from class_folder.catalog import Class_Catalog
class_automobile = Class_Automobile()
class_catalog = Class_Catalog()
..
Inside automobile.py have this code:
from pdo_mysql import Pdo_Mysql
class Class_Automobile(object):
"""
Description
"""
__pdo_mysql_intern = None
def __init__(self):
self.__pdo_mysql_intern = Pdo_Mysql('host', 'user', 'password', 'db')
## End def init
def Validate_Make(self, make_search):
query_make = 'make_text = %s '
result = self.__pdo_mysql_intern.select('make', query_make, 'id_make', make=make_search)
return result
## End function Validate Make
And inside the catalog.py file has this information:
from pdo_mysql import Pdo_Mysql
class Class_Catalog(object):
"""
Description
"""
__pdo_mysql_intern = None
def __init__(self):
self.__pdo_mysql_intern = Pdo_Mysql('host', 'user', 'password', 'db2')
## End def init
And finally the pdo_mysql.py file has this:
import MySQLdb, sys
class Pdo_Mysql(object):
"""
Description
"""
__instance = None
__host = None
__user = None
__password = None
__database = None
__session = None
__connection = None
def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = super(Pdo_Mysql, cls).__new__(cls,*args,**kwargs)
return cls.__instance
## End __new__
def __init__(self, host='localhost', user='root', password='', database=''):
self.__host = host
self.__user = user
self.__password = password
self.__database = database
## __init__
def _open(self):
try:
cnx = MySQLdb.connect(self.__host, self.__user, self.__password, self.__database)
self.__connection = cnx
self.__session = cnx.cursor()
except MySQLdb.Error as e:
print "Error %d: %s" % (e.args[0],e.args[1])
## End function open
def _close(self):
self.__session.close()
self.__connection.close()
## End function close
def select(self, table, where=None, *args, **kwargs):
result = None
query = 'SELECT '
keys = args
values = tuple(kwargs.values())
l = len(keys) - 1
for i, key in enumerate(keys):
query += "`"+key+"`"
if i < l:
query += ","
## End for keys
query += 'FROM %s' % table
if where:
query += " WHERE %s" % where
## End if where
self._open()
self.__session.execute(query, values)
result = [item[0] for item in self.__session.fetchall()]
self._close()
return result
## End function select
When I run the example.py I have this problem, somehow when I call this
validate_information = class_automobile.Validate_Make(make)
I obtain this response
File "/Library/Python/2.7/site-packages/MySQLdb/cursors.py", line 205, in execute
self.errorhandler(self, exc, value)
File "/Library/Python/2.7/site-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
raise errorclass, errorvalue
_mysql_exceptions.ProgrammingError: (1146, "Table 'db2.make' doesn't exist")
This problem it is because the db from Class_Catalog is mixing when I'm calling the Class_Automobile.
It is solved when I put the Class_Catalog before the Class_Automobile but I want to know how to solve properlly.
Update:
Thanks to Adam Smith found the problem. I just had to change this file pdo_mysql.py:
class Pdo_Mysql(object):
# all your existing definitions
def __new__(cls, *args, **kwargs):
**if not cls.__instance or not cls.__database:**
cls.__instance = super(Pdo_Mysql, cls).__new__(cls,*args,**kwargs)
return cls.__instance
Here is what's happening. When you call class_automobile = Class_Automobile() a new Class_Automobile instance is created and the __init__ method is called on that instance. Inside __init__ you're calling Pdo_Mysql('host', 'user', 'password', 'db'). Pdo_Mysql.__new__ is invoked with Pdo_Mysql as the cls variable and ('host', 'user', 'password', 'db') as the args variable. Because this is the first time a Pdo_Mysql object has been requested the class variable __instance is None so your super call is run and the result is set on the Pdo_Mysql.__instance variable. This is a class level assignment. You then return this instance. If you look at the Python docs you'll see that :
If __new__() returns an instance of cls, then the new instance’s __init__() method will be invoked
So now you've got the instance of Pdo_Mysql from the super __new__ call and you return it. The __init__ method will be called on it with the args and kwargs sent to __new__. In __init__ you set the various connection attributes.
All is fine at this point. If you were to make calls to your class_automobile instance they would work as you expected. Let's look at what happens when you make your next object, the instance of Class_Catalog.
It begins in the same way as Class_Automobile until we get to the self. __pdo_mysql_intern = Pdo_Mysql call. This time when Pdo_Mysql.__new__ is invoked the cls.__instance variable is set to an instance of Pdo_Mysql so you don't make a new one. BUT you still return this instance from __new__ so the __init__ method is still run on this instance but with new args and kwargs. The instance variables are set to the new values. So now the instances class_automobile and class_catalog have a __pdo_mysql_intern variable pointing to the same instance and that instance has __host, __user, __password, and __database set to whatever the last arguments were used to initialize an instance.
Now on to one possible solution...
It appears you're trying to do some kind of connection sharing so you don't open a new connection for each object that connects to a database. However you're only allowing one instance of Pdo_Mysql to ever exist. This means you cannot connect to more than one database and only as a single user.
You need a pool of connections (Pdo_Mysql objects) to choose from so that when you want to connect to another db or as a different user you can do so. In your Pdo_Mysql class you could change the __instance = None variable to __instances = {}. Use the args as the key to this dictionary and change the __new__ method to look up the instance using the args. Note that this ignores the kwargs, I leave it to you to figure out how to include them (hint, you cannot use mutable objects as keys in dictionaries). You'll also want to add a __initialized variable to the Pdo_Mysql class. Default it to False and set it to True in __init__. This was you can make sure you only run __init__ on each instance once.
Going a step further I would suggest you not use __new__ to create singletons but instead create a helper method in pdo_mysql.py called get_conenction or something. Have it take the same 4 arguments and it can check it's pool of instances for any that match and either return an existing connection or make a new one.
I have a problem trying to unpickle subclasses of this class. When I unpickle it, the stuff isn't there. What gives?
class Account:
def __init__(self, server, port, smtp_server, smtp_port):
self.server = server
self.port = port
self.smtp_server = smtp_server
self.smtp_port = smtp_port
self.save()
def save(self):
#save account for later loading
self.name = tkFileDialog.asksaveasfilename(title = "Save as..")
pickle.dump(self, open(self.name, "wr"))
Does your class inherit object?
Either way, you can specify what you want to pickle by overwriting __getstate__. Otherwise it should normally copy __dict__ if you're inheriting object.
So, Here's how I just figured it out- i moved the ugly pickle stuff (see comment) to the unpickling class, imported the classes I was pickling, and it seems like it works.