Jocomol
Jocomol

Reputation: 322

Python3.x self as parameter in a method call to an other class

I am trying to call a method on an other class and give the called class a reference of the current class along with some other parameters. But somehow it takes the self given as a parameter as the self of the called class.

Let me show you:

import os, sys
from wsPart import wsPart
class thermo(wsPart):
    functional = False ## see line 8
    file = '/sys/bus/w1/devices/28-00000833e8ff/w1_slave' 
    def __init__(self, name, logger):
        super().__init__(name, logger)
        functional = True 
    def read(self):
        fileobject = open(self.file)
        filecontent = fileobject.read()
        fileobject.close()
        self.logger.writeLog(self,"Completed Meassurement") ##Problem on this line
        return filecontent

So I call the class logger and the method writeLog on it. Giving the Parameters message and a reference of the class thermo (self).

import datetime
from wsPart import wsPart
class logger():
    logfile = "/var/log/wheaterstation.log"
    name = "Logger"
    def writeLog(self, sender, message):
        conn = open(self.logfile, "w")
        now = str(datetime.datetime.now().isoformat())
        conn.write("[" + now + "]" + " (" + sender.getName() + "): " + message + "\n") ##Problem on this line
        conn.close()

As you can see I put the parameters self because its a method that belongs to a class, the sender should be the reference to the class thermo that was passed as self in the thermo class. Lastly there is the message which was passed in the thermo class as well. But this just gives me the error:

Traceback (most recent call last):
File "scrLib/wsControl.py", line 61, in <module>
controller = controller()
File "scrLib/wsControl.py", line 22, in __init__
self.thermo = thermo("Thermometer", logger)
File "/home/joco/git/wheaterstation/scrLib/thermo.py", line 10, in __init__
super().__init__(name, logger)
File "/home/joco/git/wheaterstation/scrLib/wsPart.py", line 8, in __init__
self.logger.writeLog(self, "created")
TypeError: writeLog() missing 1 required positional argument: 'message'

So it seems that the self parameter which was passed in the thermo class is interpeted as the self of the class logger which gets it all mixed up.

Can you guys help me here?

The full code + additional comments can be viewed Here

Edit:
Both the logger and the thermo class get initialized in the file wsPart.py:

class controller():
    name = ""
    logger = None
    thermo = None
    dbConnector = None

    def __init__(self):
    ##THis created the controller and all the other objects
        self.name = "Controller"
        ##Create Objects
        self.logger = logger()
        self.logger.writeLog(self,"logger created") ##This line Works
        self.thermo = thermo("Thermometer", logger)
        self.dbConnector = dbConnector("DBConnector",logger)

Upvotes: 0

Views: 198

Answers (2)

Jean-Fran&#231;ois Fabre
Jean-Fran&#231;ois Fabre

Reputation: 140286

yes, bad idea to call the instance and the class name the same. Here:

    self.logger = logger()
    self.logger.writeLog(self,"logger created") ##This line Works
    self.thermo = thermo("Thermometer", logger)
    self.dbConnector = dbConnector("DBConnector",logger)

You're passing the class itself to your constructors. So the methods are seen as static/expect one more parameter. You need to change the 2 last lines to pass the instance you just created:

    self.thermo = thermo("Thermometer", self.logger)
    self.dbConnector = dbConnector("DBConnector", self.logger)

more importantly, you need to use different names for classes and instances of the same objects to avoid that confusion (python convention for class names is starting each word with upper case (camelcase) ex: Logger. Other languages don't use that convention, but python is a lot about conventions).

With a different name you'd have gotten a NameError exception and you would have fixed the error yourself.

Aside: don't "initialize" members like this in the class definition:

name = ""
logger = None
thermo = None
dbConnector = None

those are creating class members, not instance members. Remove those, and let __init__ create instance members like you're currently doing. __init__ is called no matter what, and those lines above just add to the confusion (except for some corner cases, only constants should be declared that way)

Upvotes: 1

bruno desthuilliers
bruno desthuilliers

Reputation: 77932

Totally unrelated but code in comments is unreadable so I post this as an answer:

  1. this does not work as you seem to expect:

    class Whatever(): functional = False ## see line 8

    def __init__(self, name, logger):
        super().__init__(name, logger)
        functional = True 
    

Python has no "implied this" sor here in __init__ you're not creating an instance attribute but a local variable. You want self.functional = True

  1. Make sure you close files

    def read(self): fileobject = open(self.file) filecontent = fileobject.read() fileobject.close()

If anything wrong happens between open() and fileobject.close(), the file is not garanteed to be properly closed. You want eiher a try/finally block ie

    f = open(path)
    try:
        do_something_with(f)
    finally:
        f.close()

or much better a with block:

    with open(path) as f:
        do_something_with(f)

which will ensure the file is closed whatever happens.

  1. write mode truncates the file

    def writeLog(self, sender, message): conn = open(self.logfile, "w") now = str(datetime.datetime.now().isoformat()) conn.write("[" + now + "]" + " (" + sender.getName() + "): " + message + "\n") ##Problem on this line conn.close()

as documented, opening a file in write mode truncates the file. You probably want the "append" mode instead here.

  1. Don't reinvent the squared wheel when there's a round one already

Logging is not as trivial as just writing to a file (concurrency issues, need to send the log message to some other destination, logging levels etc), and even if you don't need more (at least the moment) your solution is quite inefficient (opening a file is expensive).

Python has a very comprehensive logging package in it's standard lib. I wholefully agree that it requires a bit of learning to configure and use properly but that's still a huge win compared to the time you'll spend trying to make a naive half-backed custom implementation works properly on production, AND this is a knowledge that you will need for just any serious project anyway.

Upvotes: 0

Related Questions