TypeError: object does not support item assignment

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
TheGrudge
User
Beiträge: 96
Registriert: Donnerstag 4. Mai 2006, 18:39

Ok es ist spät am Abend, ja SEHR spät, aber irgendwie kapiere ich nicht was hier nicht funktioniert:

Code: Alles auswählen

from pysnmp.entity.rfc3413.oneliner import cmdgen
from pysnmp.proto import rfc1902
from datetime import timedelta
from webtools.blras01.models import MIB,User


class StatFile(object):
	def __init__(self,file):
		self.users = {}
		self.errorMsg = None
		self.colors = [
			'#f00',
			'#ff3200',
			'#ff6400',
			'#ff9600',
			'#ffc800'
		]
		
		self.readStatFile(file)
		self.updateDB()
		
		
	def readStatFile(self,file):
		try:
			f = open(file,"r")
		except IOError, error:
			self.errorMsg = error
		else:
			for line in f:
				if 'ISDN-6-DISCONNECT' in line:
					tmp = line.strip().replace(',','').split()
					username = tmp[-5]
					uptime = tmp[-2]
					if not self.users.has_key(username):
						self.users[username] = [int(uptime),1]
					else:
						# add the current uptime to total uptime
						self.users[username][0] = self.users[username][0] + int(uptime)
						# increase login counter
						self.users[username][1] += 1
			f.close()
			
			
	def userExists(self,user,names):
		"""checks if the user exists in the database"""
		if user in names:
			return True
		else:
			return False
		
	
	def updateDB(self):
		"""if a user doesn't exist in the database, insert the new user into the DB"""
		names = []
		if self.users:
			all_users = User.objects.all()
			if all_users:
				names = [ u.name for u in all_users ]
			for user in self.users:
				if not self.userExists(user,names):
						new_user = User()
						new_user.name = user
						new_user.uptime = self.users[user][0]
						new_user.save()
			
	def getSortedByUptime(self):
		"""returns a list of users found in the logfile, sorted by uptime"""
		sortedUsers = None
		if self.users:
			for user in self.users:
				# format all time info into human readable code
				self.users[user][0] = timedelta(seconds=self.users[user][0])
			# sort by uptime
			sortedUsers = sorted(self.users.items(), reverse=True, key=lambda (k,v): (v,k))
			
		if sortedUsers:
			colorIterator = iter(self.colors)
			for u in sortedUsers:
				# try to get user data from database, set fullname if user exists
				try:
					userObject = User.objects.get(name=u[0])
				except:
					pass
				else:
					u[0] = userObject.fullname
				
				# set color string
				try:
					u[1].append(colorIterator.next())
				except StopIteration:
					u[1].append('#000')
		return (self.errorMsg,sortedUsers)




##########################################################################################
# SNMP
##########################################################################################
def getSNMPValue(host,comm,oid):
		return cmdgen.CommandGenerator().getCmd(
					cmdgen.CommunityData('my-agent', comm , 1),
					cmdgen.UdpTransportTarget((host, 161)),
					tuple([int(n) for n in oid.split('.')]))
					

def getNextSNMPValue(host,comm,mib):
	return cmdgen.CommandGenerator().nextCmd(
		cmdgen.CommunityData('my-agent', comm , 1),
		cmdgen.UdpTransportTarget((host, 161)),
		tuple([int(n) for n in mib.oid.split('.')]))
		
		
		
		
		
##########################################################################################
# get online users
##########################################################################################
def getOnlineUsers():
	# variables
	users = []
	
	# check for logged in users
	online_users_mib = MIB.objects.get(shortName='blras01_online_users')
	user_uptime_mib = MIB.objects.get(shortName='blras01_user_uptime')
	errorIndication, errorStatus, errorIndex, varBindTable = getNextSNMPValue('blras01','xxxxxxxx',online_users_mib)
	
	if not errorIndication:
		if varBindTable:
			for vrow in varBindTable:
				user_name = vrow[0][1]
				user_fullname = ""
				userObject = User.objects.get(name=user_name)
				if userObject:
					user_fullname = userObject.fullname
				user_id = vrow[0][0][-2]
				user_uptime = "nix"
				errorIndication2, errorStatus2, errorIndex2, varBinds2 = getSNMPValue('blras01','xxxxxxxx',user_uptime_mib.oid+str(user_id)+".0")
				if errorIndication2:
					user_uptime = "Nix"
				user_uptime = int(varBinds2[0][1])
				# convert TimeTick to human readable text string
				d, m = divmod(user_uptime, 8640000)
				uptime_str = '%d days,<br/>' % d
				d, m = divmod(m, 360000)
				uptime_str += '%d:' % d
				d, m = divmod(m, 6000)
				uptime_str += '%02d:' % d
				d, m = divmod(m, 100)
				uptime_str += '%02d' % d
				users.append((user_name,user_fullname,user_id,uptime_str))
	return (errorIndication,users)
			
			
def getIgnoreUsers():
	lala = User.objects.filter(isIgnored=True).order_by('name')
	test = [ l.name for l in lala ]
	return lala
Das Problem liegt in getSortedByUptime:

Code: Alles auswählen

if sortedUsers:
            colorIterator = iter(self.colors)
            for u in sortedUsers:
                # try to get user data from database, set fullname if user exists
                try:
                    userObject = User.objects.get(name=u[0])
                except:
                    pass
                else:
                    u[0] = userObject.fullname
               
                # set color string
                try:
                    u[1].append(colorIterator.next())
                except StopIteration:
                    u[1].append('#000')
Ich kann einfach nicht

Code: Alles auswählen

u[0] = userObject.fullname
durchführen.
Bekomme dann immer folgenden Error:
TypeError: object does not support item assignment
Was mache ich denn falsch?
sortedUsers ist doch eine Liste, also sollte ich doch u[0] zuweisen können, weiter unten klappt es doch auch (u[1].append(colorIterator.next())).

Ich gehe wohl besser mal ins Bett, vielleicht fällt mir morgen der Fehler selber auf...
BlackJack

`sortedUsers` mag eine Liste sein, aber `u` ist ein Element dieser Liste.  Soweit ich das durch einen kurzen Blick sehen konnte ist es wohl ein Tupel.

Die Zeile mit dem `append()` wird wohl nicht ausgeführt, sonst würde es dort auch eine Ausnahme geben.

Wenn der Interpretierer behauptet ein Objekt kann etwas bestimmtes nicht, obwohl man sich sicher ist, das müsste es aber können, dann schaut man am besten mit einem ``print repr(obj), type(obj)`` mal nach, ob man an der Stelle überhaupt das Objekt hat, von dem man glaubt es müsste dort sein. :-)
BlackJack

Kleiner Nachtrag wie man diesen kleinen Ausschnitt etwas übersichtlicher, mit weniger Codezweigen schreiben kann (ungetestet):

Code: Alles auswählen

from itertools import chain, izip, repeat

        # ...
        colors = chain(self.colors, repeat('#000'))
        for user, color in izip(sorted_users, colors):
            try:
                user_object = User.objects.get(name=user[0])
                user[0] = user_object.fullname
            except Exception:   # Hier die erwartete Ausnahme hinschreiben!
                pass
            
            user[1].append(color)
        # ...
Generell sollte man nicht einfach nur ``except:`` benutzen weil man so *alle* Ausnahmen an dieser Stelle ausblendet, auch welche die man zur Fehlersuche lieber gesehen hätte.
Benutzeravatar
TheGrudge
User
Beiträge: 96
Registriert: Donnerstag 4. Mai 2006, 18:39

hmm also die Zeile mit
u[1].append
wird ausgeführt, das sehe ich ja in der Weboberfläche, denn die ersten 5 Einträge sind rötlich, der Rest schwarz, das funktioniert ja komischerweise...
Nirven
User
Beiträge: 130
Registriert: Mittwoch 10. Mai 2006, 08:18
Wohnort: Bremerhaven

Füg doch mal vor der Zeile `print repr(obj), type(obj)` ein, wie BlackJack es vorgeschlagen hat.

Du iterierst über die Liste sortedUsers. Ein Element der Liste ist u, und u umfasst offensichtlich wieder mehrere Elemente. Das zweite Element scheint wieder eine Liste zu sein (sonst würde das u[1].append wohl nicht funktionieren).
Aber daraus kann ich keinen Rückschluss darauf ziehen, was u[0] ist. Die Ausgabe des print sollte da eine Antwort liefern, poste das dochmal, dann kann dir schon eher jemand helfen.
Benutzeravatar
TheGrudge
User
Beiträge: 96
Registriert: Donnerstag 4. Mai 2006, 18:39

u[0] = '10149901-router' <type 'str'>

Also ein String. Die Zuweisung ging irgendwie immer, ich weiss nicht was ich geändert habe, aber vielleicht hilft ja ein

Code: Alles auswählen

u[0] = str(userObject.fullname)
weiter...

Nee sehe grad dann kommt der selbe error, ich stehe total aufm Schlauch, so schwer kann das doch nicht sein... ich glaube ich brauche mal 'nen Kaffee! :wink:
Benutzeravatar
TheGrudge
User
Beiträge: 96
Registriert: Donnerstag 4. Mai 2006, 18:39

Ich habe es jetzt ein wenig mehr OOP programmiert und nun funktioniert es, so finde ich es auch viel besser gelöst, alleine die Ausgabe als timedelta war in der alten Version schrecklich! :roll:

Code: Alles auswählen

from datetime import timedelta
# django imports
from webtools.blras01.models import User

class LogFile(object):
	"""this class represents any kind of logfile"""
	def __init__(self,file):
		self.filename = file
		self.f = open(self.filename,"r")
	
	def readFileStream(self):
		"""return a generator that prints out the content of the file line by line"""
		return ( line for line in self.f )


class UserInfo(object):
	def __init__(self,username,logins=0,uptime=0):
		self.username = self.getFullName(username)
		self.logins = int(logins)
		self.uptime = int(uptime)
		
	def addLogin(self,login=1):
		self.logins += int(login)
		
	def addUptime(self,uptime):
		self.uptime += int(uptime)
		
	def getUptime(self):
		return timedelta(seconds=self.uptime)
	
	def getFullName(self,username):
		try:
			userObject = User.objects.get(name=username)
		except User.DoesNotExist:
			pass
		else:
			if userObject.fullname:
				return userObject.fullname
			else:
				return username
	
	def __str__(self):
		return "<UserInfo name: %s logins: %d uptime: %d>" % (self.username,self.logins,self.uptime)



class StatFile(LogFile):
	def __init__(self,file,init=True,updatedb=True):
		LogFile.__init__(self,file)
		self.users = dict()
		self.colors = [
			'#f00',
			'#ff3200',
			'#ff6400',
			'#ff9600',
			'#ffc800'
		]
		if init:
			self.init()
		if updatedb:
			self.updateDB()
		
	def init(self):
		"""init the StatFile-Object: Read the logfile and fetch User Information"""
		for line in self.readFileStream():
			if 'ISDN-6-DISCONNECT' in line:
				tmp = line.strip().replace(',','').split()
				username = tmp[-5]
				uptime = tmp[-2]
				if not self.users.has_key(username):
					self.users[username] = UserInfo(username,1,uptime)
				else:
					# add the current uptime to total uptime
					self.users[username].addUptime(uptime)
					# increase login counter
					self.users[username].addLogin()
			
	def updateDB(self):
		"""if a user doesn't exist in the database, insert the new user into the DB"""
		if self.users:
			names = []
			all_users = User.objects.all()
			if all_users:
				names = [ u.name for u in all_users ]
			for user in self.users:
				if not user in names:
					new_user = User()
					new_user.name = user
					new_user.save()
	
	def getSortedByUptime(self):
		"""returns a list of users found in the logfile, sorted by uptime"""
		sortedUsers = list()
		if self.users:
			sortedUsers = [ (self.users[u].uptime, self.users[u]) for u in self.users ]
			sortedUsers.sort(reverse=True)
		return sortedUsers
		
		
	def getSortedByLogins(self):
		"""returns a list of users found in the logfile, sorted by uptime"""
		sortedUsers = list()
		if self.users:
			sortedUsers = [ (self.users[u].logins, self.users[u]) for u in self.users ]
			sortedUsers.sort(reverse=True)
		return sortedUsers
Ist der Code in Ordnung oder kann man da noch Sachen verbessern / optimieren?
Ich bin noch relativ neu in Python und erst recht in OOP, vielleicht kann man ja doch was "schlauer" lösen?
Falls nicht danke ich herzlich allen die mir geholfen haben! :D


Habe gerade hier einen Thread gelesen der über Getters/Setters herzieht.
Wäre es dann Python stilistisch besser diese Funktionen wegzulassen:

Code: Alles auswählen

def addLogin(self,login=1):
		self.logins += int(login)
		
	def addUptime(self,uptime):
		self.uptime += int(uptime)
Das sind im Grunde ja nur Setters, und eigentlich kann man die Zuweisung auch manuell vornehmen...
Wobei

Code: Alles auswählen

def getUptime(self):
		return timedelta(seconds=self.uptime)
nicht zu ersetzen ist, oder gibt es auch eine Möglichkeit zu sagen das wenn ich Object.uptime drucken will, das daraus ein timedelta erzeugt werden soll? Fand die Methode eigentlich sehr praktisch und logisch...
BlackJack

Die beiden üblichen "Verstösse" gegen PEP 008: Einige Zeilen sind länger als 80 Zeichen und die Methodennamen sind Java-like und nicht worte_durch_unterstriche_getrennt. :-)

Und es sind ein paar Leerzeichen nach Kommata zu wenig.

Die Klasse LogFile erscheint mir überflüssig. Die bietet nichts was das eingebaute `file` nicht auch hat. Und das kann man zusätzlich auch wieder schliessen.

Code: Alles auswählen

In [3]: f = open('test.txt', 'r')

In [4]: f.name
Out[4]: 'test.txt'
In `User.getFullname()` könnte man den ``else``-Zweig etwas vereinfachen:

Code: Alles auswählen

    def getFullName(self,username): 
        try: 
            userObject = User.objects.get(name=username) 
        except User.DoesNotExist: 
            pass 
        else: 
            return userObject.fullname or username
Das ist aber Geschmackssache.

Anstelle von `__str__()` hätte man `__repr__()` auf diese Weise implementieren können. Da alle Werte die Ausgegeben werden, auch in der Signatur von `__init__()` vorkommen, könnte man sogar eine Zeichenkette erzeugen die sich per ``eval`` wieder zu einem Objekt auswerten liesse.

Den Test ob ein Schlüssel in einem Dictionary enthalten ist oder nicht, kann auch mit ``in`` bzw. ``not in`` ausdrücken, was den Vorteil hat, das dieser Test bei mehr Datenstrukturen funktioniert, z.B. auch bei `set` und `list`, und nicht nur bei solchen die eine `haskey()` Methode besitzen.

`updateDB()` kann man mit `set`\s etwas kürzer und auch effizienter schreiben:

Code: Alles auswählen

    def updateDB(self): 
        if self.users: 
            existing_users = set(user.name for user in User.objects.all())
            for username in (set(self.users) - existing_users):
                user = User()
                user.name = username
                user.save()
Die `getSortedBy*()` Methoden liessen sich auch wie folgt schreiben, allerdings gibt's dann keine Tupel mehr, sondern nur noch die `UserInfo` Objekte in der gewünschten Reihenfolge:

Code: Alles auswählen

from operator import attrgetter

    # ...

    def getSortedByUptime(self): 
        """returns a list of users found in the logfile, sorted by uptime""" 
        return sorted(self.users.values(),
                      key=attrgetter('uptime'),
                      reverse=True)
Benutzeravatar
TheGrudge
User
Beiträge: 96
Registriert: Donnerstag 4. Mai 2006, 18:39

Ich sehe ich muss noch einiges lernen... :-)

Habe mal das Buch "Learning Python" durchgelesen, aber da steht nicht allzuviel über Codingstyle drin.

Das mit LogFile bin ich noch am überlegen, ich brauche mehrere LogFile-Typen, vielleicht werde ich sie noch erweitern. Wenn nicht fliegt sie natürlich wieder raus. Deine anderen Vorschläge werde ich mir mal anschauen, vielen vielen Dank!

Ist echt 'ne klasse Community hier! :D
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

TheGrudge hat geschrieben:Habe mal das Buch "Learning Python" durchgelesen, aber da steht nicht allzuviel über Codingstyle drin.
Lies doch einfach direkt die PEP 8, die ist für meine Begriffe gut verständlich.

So etwas von vorn herein hätte diversen anderen Programmiersprachen *sehr* gut getan. Letztlich ist es natürlich auch am Programmierer, sich daran zu halten, aber gemeinsame Code Conventions kann man auch irgendwo als Sprachfeature ansehen, das Python anderen überlegen macht.
Benutzeravatar
TheGrudge
User
Beiträge: 96
Registriert: Donnerstag 4. Mai 2006, 18:39

bin ja schon dabei... :D und werde versuchen meinen bisherigen Code ein wenig zu verbessern...
Benutzeravatar
TheGrudge
User
Beiträge: 96
Registriert: Donnerstag 4. Mai 2006, 18:39

BlackJack hat geschrieben: In `User.getFullname()` könnte man den ``else``-Zweig etwas vereinfachen:

Code: Alles auswählen

    def getFullName(self,username): 
        try: 
            userObject = User.objects.get(name=username) 
        except User.DoesNotExist: 
            pass 
        else: 
            return userObject.fullname or username
Das ist aber Geschmackssache.
Geht das denn? Wenn try fehlschlägt und nicht in den else-Zweig springt, dann liefert die Funktion doch None zurück... oder irre ich mich da?
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Es macht zumindest nix anderes als die von dir gepostete Funktion.
Benutzeravatar
TheGrudge
User
Beiträge: 96
Registriert: Donnerstag 4. Mai 2006, 18:39

birkenfeld hat geschrieben:Es macht zumindest nix anderes als die von dir gepostete Funktion.
hmm stimmt! :D aber es funktioniert bis jetzt, liegt wohl daran das es die Userobjekte schon gibt.

Ich denke da muss wohl noch ein return rein:

Code: Alles auswählen

def getFullName(self,username):
        try:
            userObject = User.objects.get(name=username)
        except User.DoesNotExist:
            pass
        else:
            return userObject.fullname or username
        return username
Antworten