I have two python class objects that I use to extract data from Oracle to ArcMap. The various activities in the process cause me to start with a list of 'column' objects and build a pyTable object. The pyTable object has a list of insert Fields by name. During __init__ I use the getSelect() function to populate the list of insert Fields.
I have added a bunch of statements to make sure that each time I call pyTable I get a newly created object, but I'm still seeing a strange result. The first time I use the class, all is fine. The second time I issue the same statement, the colList is new, but the field list is duplicated. My apologies for not cleaning out the extraneous code sections.
Where am I messing up my object references?
Here are the execution results. myList has 8 column objects.
>>> arcTable = pyTable(myList)
>>> len(arcTable.getTuple())
8
>>> arcTable = pyTable(myList)
>>> len(arcTable.getTuple())
16
>>> arcTable = pyTable(myList)
>>> len(arcTable.getTuple())
8
>>> arcTable = pyTable(myList)
>>> len(arcTable.getTuple())
8
>>> newTable = pyTable(myList)
>>> len(newTable.getTuple())
8
>>> thirdTable = pyTable(myList)
>>> len(thirdTable.getTuple())
16
>>> thirdTable = pyTable(myList)
>>> len(thirdTable.getTuple())
24
>>> thirdTable = pyTable(myList)
>>> len(thirdTable.getTuple())
8
>>>
Here are the two classes:
import arcpy, cx_Oracle
class column:
# Add to the arcType and cxType functions to support more Oracle data types.
# BLOB and CLOB fields will need additional support in Read and Write fx's.
name = ''
dataType = ''
dataLen = 1
dataPrecision = 0
dataScale = 0
query = ''
isShape = False
isLOB = False
def __init__(self, Name, DataType, DataLen, DataPrecision, DataScale):
self.name = Name
self.dataType = DataType
self.dataLen = DataLen
self.dataPrecision = DataPrecision
self.dataScale = DataScale
if DataType == 'WKT':
self.query = 'sdo_util.to_wktgeometry(t.' + Name + ') wkb, '
else:
self.query = 't.' + Name
if DataType == 'SDO_GEOMETRY':
self.isShape = True
if DataType == 'BLOB' or DataType == 'CLOB' or DataType == 'WKT':
self.isLOB = True
def getArcType(self, *args): # Data type translation 'Oracle_type':'ESRI_type'
return {
# 'BINARY_DOUBLE':'DOUBLE',
# 'BINARY_FLOAT':'FLOAT',
# 'BLOB':'BLOB',
'CHAR':'STRING',
'CLOB':'CLOB',
'DATE':'DATE',
# 'FLOAT':'FLOAT',
# 'LONG':'LONG',
# 'LONG RAW':'BLOB',
'NUMBER':'DOUBLE',
# 'RAW':'BLOB',
# 'ROWID':'SHORT',
'SDO_GEOMETRY':'GEOMETRY',
'VARCHAR2':'STRING',
'WKT':'WKT',
}.get(self.dataType,"undefined")
def getCxType(self, *args): # Data type translation 'Oracle_type':'cx_Oracle.type'
return {
'BLOB':cx_Oracle.BLOB,
'CHAR':cx_Oracle.STRING,
'CLOB':cx_Oracle.CLOB,
'DATE':cx_Oracle.DATETIME,
'NUMBER':cx_Oracle.NUMBER,
'SDO_GEOMETRY':cx_Oracle.CLOB,
'VARCHAR2':cx_Oracle.STRING,
}.get(self.dataType,"undefined")
class pyTable:
# Create an object to track columns for read and write operations.
# BLOB, CLOB and SDO_GEOMETRY types will need additional support in Read and Write fx's.
length = 0
# colList = [] # The original list of columns is coming from an Oracle query.
# These two lists are different because of the way I treat shape.
# I create a FC and then add attribute columns. This puts the Shape column first in the list.
__insCols = [] # I use insCols as a list of column type objects to write to ArcMap.
__insertFields = []
__colTuple = None
__myData = []
__pKey = 'P_KEY' # The name of the primary key field should be <table>_CN
__insBlobCols = [] # A list of column positions that contain BLOB data types.
__insKeyCol = -1 # The position of the primary key column.
def __init__(self, ColList):
self.colList = ColList[:]
self.length = len(ColList)
self.isFC = self.__getShape()
self.__select = self.getSelect()
arcpy.AddMessage('New table class created with ' + str(self.length) + ' columns.')
def __del__(self):
self.colList = []
del self.__insCols [:]
del self.__insertFields [:]
del self.__myData [:]
del self.__insBlobCols [:]
def addDataRow(self, inDataRow):
self.__myData.append(inDataRow)
def getInsCols(self):
return self.__insCols
def getTuple(self):
return self.__colTuple
def getPK(self):
return self.__pKey
def getInsBlobCols(self):
return self.__insBlobCols
def clearData(self):
self.__myData = []
def getData(self):
return self.__myData
def getKeyCol(self):
return self.__insKeyCol
def __getShape(self):
isFeature = False
featureName = ''
for col in self.colList:
if col.isShape:
isFeature = True
featureName = col.name
if isFeature:
wktShape = column(featureName, 'WKT', 0, 0, 0)
self.__insCols.append(wktShape)
for col in self.colList:
if not col.isShape:
self.__insCols.append(col)
return isFeature
def getSelect(self):
# Build the select statement
# Build the list of insert Field names
# Build the Tuple of insert Field names
# Identify the LOB columns by index number
statement = 'select '
del self.__insertFields[:]
indx = 0
# print ('Table has ', len(self.__insCols), ' insert columns.')
for col in self.__insCols:
if col.dataType == 'WKT':
statement += 'sdo_util.to_wktgeometry(t.shape) wkb, '
self.__insertFields.append('SHAPE#WKT')
else:
statement += 't.' + col.name + ', '
self.__insertFields.append(col.name)
if col.dataType == 'BLOB':
self.__insBlobCols.append(indx)
#
# ToDo: The key column should be <table>_CN
# But, the logic needs to work for views with different names.
#
if col.name == self.__pKey:
self.__insKeyCol = indx
indx += 1
statement = statement[:statement.rfind(',')] # Trim off the trailing comma
# print ('Insert is composed of ', len(self.__insertFields), ' fields.' )
self.__colTuple = tuple(self.__insertFields)
return statement
def createTemp(self, WorkSpace, tempFC):
success = False
insertCols = self.__insCols
arcpy.AddMessage('Adding ' + tempFC + ' with ' + str(len(insertCols)) + ' columns.')
try:
if self.isFC:
arcpy.CreateFeatureclass_management(WorkSpace, tempFC, 'POINT')
arcpy.AddMessage(tempFC + ' feature class was successfully created.')
else:
arcpy.CreateTable_management(WorkSpace, tempFC)
arcpy.AddMessage(tempFC + ' table was successfully created.')
for col in insertCols:
esriType = col.getArcType()
if esriType == "undefined":
arcpy.AddError('Data type not currently supported, ' + col.dataType)
return success
if col.dataType <> 'WKT':
arcpy.AddField_management(tempFC, col.name, esriType, col.dataPrecision, col.dataScale, col.dataLen)
arcpy.AddMessage('Created column: ' + col.name)
success = True
except:
e = sys.exc_info()[1]
arcpy.AddError('Create of ' + tempFC + ' failed with ' + str(e.args[0]))
return success
You are making a shallow copy of the list passed to your class in the init function.
See Shallow and deep copy operations in the Python documentation for some basic information.
self.colList = ColList[:] makes a new LIST, but inside that new list are references to the same objects that were in the original list (a shallow copy).
You need a deep copy:
import copy
...
self.colList = copy.deepcopy(ColList)
A deep copy has a new list, as well as new objects initialized to match the objects in the original list. So if the objects in one class' list change, they don't change in every class.
Related
I'd like to read combined information from of a bunch of variables.
I tried this here:
class MyStruct:
first_byte = 0
second_byte = 0
combined = str(hex(first_byte)) + " " + str(hex(second_byte))
test = MyStruct() test.first_byte = 36 test.second_byte = 128
print("MyStruct: test first=%i second=%i comb=%s" %(test.first_byte, test.second_byte, test.combined))
And I get:
>
MyStruct: test first=36 second=128 comb=0x0 0x0
But I was expecting:
>
MyStruct: test first=36 second=128 comb=0x24 0x80
I see that the calculations of them combined are made just when it is declared. But I don't know how to calculate it again.
Why I am using this:
I'd like to create a protocol-string for LIN-signals over a CARBERRY
Here you can find further information: LIN-command
I'd like to define each byte separately like this:
protokol.pid = 10
protokol.D0 = value_one
protokol.D1 = value_two
and so on..
Well, simply put, the line combined = str(hex(first_byte)) + " " + str(hex(second_byte))
is evaluated when you create the class.
What you could do is the following:
class MyStruct:
def __init__(self, first_byte, second_byte):
self.first_byte = first_byte
self.second_byte = second_byte
self.combined = hex(first_byte) + " " + hex(second_byte)
test = MyStruct(36, 128)
print("MyStruct: test first=%i second=%i comb=%s" %(test.first_byte, test.second_byte, test.combined))
I suggest you take a look at classes, methods and attributes before you go any further, you may pick up a few useful tools.
Further explanation:
By declaring first_byte and second_byte directly inside the class, you are creating static class variables, accessed by MyStruct.first_byte and MyStruct.second_byte. Here's an example to illustrate what I'm talking about:
>>> class A:
tag = 10
def set_tag(self, val):
self.tag = val
>>> b = A()
>>> b.tag
10
>>> A.tag
10
>>> b.set_tag(5)
>>> b.tag
5
>>> A.tag
10
I got a question regarding list within python. I use the append method to actually append values to my list, now it only replaced the list with new values.
This is my code:
def init(serial):
serial_number = serial
api_call = "http://wwww.herecomesmyhyperlink/"+serial_number
result = []
with open('allserials.csv') as csvfile:
reader = csv.reader(csvfile, delimiter=';', quotechar='|')
for row in reader:
if row[0].strip() == api_call:
result.append(row[1].strip())
call_api(serial_number,result)
return
def call_api(get_serial,get_result):
list_serial = []
for i in range(len(get_result)):
# do an api call
....
# get result of api call
list_serial.append(api_result)
sort_serials(list_serial)
return
def sort_serials(get_list_serial)
sorted_list_serial = sorted(get_list_serial, reverse=True)
print(sorted_list_serial)
max_results = 10
length_of_sorted_list_serial = len(get_list_serial)
if length_of_sorted_list_serial < max_results:
get_first_list_element = sorted_list_serial[0]
get_second_element_of_that_list = get_first_list_element[1]
init(get_second_element_of_that_list)
else:
print("it is not smaller")
return
print(init('1320739'))
sorted_list_serial would contain something like: [rankingid,serial,title].
get_second_element_of_that_list: [serial]
The thing is that when I run my code I got the following results:
s: 1320739, max result:10 length of the list:3
s: 1523039, max result:10 length of the list:9
What the code does is that instead of having a list of 12 items, it replace the list with the 3 items with the new list of 9 items.
What I want is to actually have a new list containing 12 items, so that the first 3 items are still within the list and the 9 other elements are added to the original list.
The list is scoped to the function call_api() so pull it out, or pass it to each function, or create a class.
def init(serial):
serial_number = serial
result = []
with open('allserials.csv') as csvfile:
result.append()
return result
def call_api(get_serial,get_result):
# list_serial = []
#
# Move this out
# Or pass it along to each function
for i in range(len(get_result)):
# do an api call
....
# get result of api call
list_serial.append(api_result)
return list_serial
def sort_serials(get_list_serial)
sorted_list_serial = sorted(get_list_serial, reverse=True)
max_results = 10
length_of_sorted_list_serial = len(get_list_serial)
if length_of_sorted_list_serial < max_results:
get_first_list_element = sorted_list_serial[0]
get_second_element_of_that_list = get_first_list_element[1]
else:
print("it is not smaller")
return {'get_second_element_of_that_list':get_second_element_of_that_list, 'sorted_serial_list':sorted_serial_list}
So scope it to the same function, and have the other functions return results:
def run():
list_serial = []
serial_number = '1320739'
result = init(serial_number)
# here the items get set
list_serial = call_api(serial_number,result)
# here they get sorted
serial_sorted = sort_serials(list_serial)
# list serial is now the sorted list
list_serial = serial_sorted['sorted_serial_list']
get_second_element_of_that_list = serial_sorted['get_second_element_of_that_list']
init(get_second_element_of_that_list)
Or redefine how its passed:
serial_number = '1320739'
init(serial_number, list_serial)
call_api(serial_number,result, list_serial)
sort_serials(list_serial)
init(get_second_element_of_that_list, list_serial)
Or just pull it out:
.
.
.
list_serial = []
print(init('1320739'))
Or create a class:
class SomeClassNameHere(object):
def __init__(self,serialnumber=None, item2=''):
self.serialnumber = serialnumber
self.item3 = item2
self.listserial = []
self.run(item2)
def doOtherStuff(self):
# self.listserial will be updated
self.listserial = [1,2,3]
print(self.item3)
print(self.serialnumber)
def run(self,passeditem2):
print('Item2 has been passed: {0}'.format(passeditem2))
print('listserial not updated:',self.listserial)
self.doOtherStuff()
print('listserial updated:',self.listserial)
here = SomeClassNameHere(serialnumber='456',item2='somestring')
print(here.serialnumber)
print(here.item3)
here.run()
here.doOtherStuff()
I'm trying to learn how to correctly use classes in python, I'm fairly new to it but I cant get this class to return a string output of all the values. Ideally I'd like to be able to just str(packet) into a network socket.
class ARP():
dst_addr = ''
src_addr = ''
type = '\x08\x06'
payload = '\x00\x01\x08\x00\x06\x04\x00'
arptype = '\x01'
src_mac_addr = ''
src_ip_addr = ''
dst_mac_addr = ''
dst_ip_addr = ''
def __repr_(self):
return 'ARP'
def __str__(self):
return dst_addr + src_addr + type + payload + arptype \
+ src_mac_addr + src_ip_addr + dst_mac_addr + dst_ip_addr
p = ARP()
p.dst_addr = router_mac
p.src_addr = random_mac()
p.arptype = '\x02'
p.src_mac_addr = local_mac
p.src_ip_addr = ip_s2n(target_ip)
p.dst_mac_addr = router_mac
p.dst_ip_addr = ip_s2n(router_ip)
print 'PACKET: ', str(p)
return str(p)
This code outputs nothing at all. repr() outputs <__main__.ARP instance at 0x2844ea8> which I guess is what it's meant to do?
You are missing an underscore in your __repr__ method name:
def __repr_(self):
# ---------^
Python looks for __repr__, not __repr_.
Next, your __str__ method should refer to attributes on self, not to globals. Perhaps a str.join() call would be helpful here too:
def __str__(self):
return ''.join([getattr(self, attr) for attr in (
'dst_addr', 'src_addr', 'type', 'payload', 'arptype', 'src_mac_addr',
'src_ip_addr', 'dst_mac_addr', 'dst_ip_addr')])
This is a class which will take in as input and then output a polynomial in string form (both ways same format). Some arithmetic is performed in the various methods. I've been trying to inherit this class into another class that will then use the __mod__() special method of the first class (or make it's own special method if necessary but I don't see how you can't just use the original method) to perform the mod on intake. Seems like this goes into __init__() but I've tried 5 different versions of this, even going so far as to change the parent class, and I'm getting nowhere. I'm teaching myself Python so I'm sure that even a junior Python dev can see where I'm going totally wrong.
import re
class GF2Polynomial(object): #classes should generally inherit from object
def __init__(self, string):
'''__init__ is a standard special method used to initialize objects.
Here __init__ will initialize a gf2infix object based on a string.'''
self.string = string #basically the initial string (polynomial)
self.key,self.lst = self.parsePolyVariable(string) # key determines polynomial compatibility
self.bin = self.prepBinary(string) #main value used in operations
def id(self,lst):
"""returns modulus 2 (1,0,0,1,1,....) for input lists"""
return [int(lst[i])%2 for i in range(len(lst))]
def listToInt(self,lst):
"""converts list to integer for later use"""
result = self.id(lst)
return int(''.join(map(str,result)))
def parsePolyToListInput(self,poly):
"""
replaced by parsePolyVariable. still functional but not needed.
performs regex on raw string and converts to list
"""
c = [int(i.group(0)) for i in re.finditer(r'\d+', poly)]
return [1 if x in c else 0 for x in xrange(max(c), -1, -1)]
def parsePolyVariable(self,poly):
"""
performs regex on raw string, converts to list.
also determines key (main variable used) in each polynomial on intake
"""
c = [int(m.group(0)) for m in re.finditer(r'\d+', poly)] #re.finditer returns an iterator
letter = [str(m.group(0)) for m in re.finditer(r'[a-z]', poly)]
m = max(c); varmatch = True; key = letter[0]
for i in range(len(letter)):
if letter[i] != key: varmatch = False
else: varmatch = True
if varmatch == False: return "error: not all variables in %s are the same"%a
d = [1 if x in c else (1 if x==0 else (1 if x=='x' else 0)) for x in xrange(m, -1, -1)]
return key,d
def polyVariableCheck(self,other):
return self.key == other.key
def prepBinary(self,poly):
"""converts to base 2; bina,binb are binary values like 110100101100....."""
x = self.lst; a = self.listToInt(x)
return int(str(a),2)
def __mod__(self,other):
"""
__mod__ is the special method for overriding the % operator
returns remainder formatted as polynomial
"""
if self.polyVariableCheck(other) == False:
return "error: variables of %s and %s do not match"%(self.string,other.string)
if self.bin == other.bin: return 0
return GF2Polynomial(self.outFormat(self.bin%other.bin))
def __str__(self):
return self.string
def outFormat(self,raw):
"""process resulting values into polynomial format"""
raw = "{0:b}".format(raw); raw = str(raw[::-1]); g = [] #reverse binary string for enumeration
g = [i for i,c in enumerate(raw) if c == '1']
processed = "x**"+" + x**".join(map(str, g[::-1]))
proc1 = processed.replace("x**1","x"); proc2 = proc1.replace("x**0","1")
if len(g) == 0: return 0 #return 0 if list empty
return proc2 #returns result in gf(2) polynomial form
The desired result is to be able to call it on a new (child) class with the parent type and while changing the parent class as little as possible (if even at all). Note that class "BinaryField" is the intended child class:
p=GF2Polynomial("x**2+x**1+x**0")
a=BinaryField("x**1+x**0", p)
b=BinaryField("x**1", p)
On intake, the given polynomial should be modulus divided by the 2nd element (here it's 'p'). This is necessary for finite field math.
EDIT:
when running it with --
## "x**1 + x**0" polynomial string style input
poly1 = "x**14 + x**1 + x**0"; poly2 = "x**6 + x**2 + x**1"; poly3 = "y**6 + y**2 + y**1"
a = GF2Polynomial(poly1); b = GF2Polynomial(poly2); c = GF2Polynomial(poly3)
## "x+1" polynomial string style input
poly4 = "x**14 + x + 1"; poly5 = "x**6 + x**2 + x"; poly6 = "y**6 + y**2 + 1"
d = GF2Polynomial(poly4); e = GF2Polynomial(poly5); f = GF2Polynomial(poly6)
bf1 = BinaryField(poly1,b); print bf1
bf2 = BinaryField(poly4,e); print bf2
Both of these styles are possible because of the way I coded it, but they should both return the same answer. However the result on that code is:
>>>
x**5 + x**4 + x**3 + 1
x**5 + x
Also, when using BinaryField(poly4,d), which is just the same string with it's GF2Polynomial() initialization, this errors as:
AttributeError: 'int' object has no attribute 'string'
Does this solves your problem?
class BinaryField(GF2Polynomial):
def __init__(self, string, mod):
modded = GF2Polynomial(string) % mod
super(BinaryField, self).__init__(modded.string)
>>> p = GF2Polynomial("x**2+x**1+x**0")
>>> a = BinaryField("x**1+x**0", p)
>>> print a
x + 1
You can also make the BinaryField class to be just a factory method:
def BinaryField(string, mod):
return GF2Polynomial(string) % mod
It is time to make my first question here.
I am facing the following issue:
i am using the pickle module to dump a large dictionary and then load it from the disk back.
The problem is that after unpickling the two objects are not the same.
Actually this is what I am doing:
In the file A.py I define my class that has some attributes and methods.
In file B.py I make a dictionary where the values are instances of the class in A.py
Also in file B.py I pickle this dictionary and unpickle it again.
The two dicts are not the same. I checked the keys and are the same. The problem lies with the values.
Any ideas?
Two different dictionaries with the same keys & values would not be considered identical:
>>> d1 = {'k1': 12345}
>>> d2 = {'k1': 12345}
>>> d1 is d2
False
>>> d1['k1'] is d2['k1']
False
Even if you actually copy the values from one to the other:
>>> d1['k1'] = d2['k1']
>>> d1['k1'] is d2['k1']
True
>>> d1 is d2
False
That's because each one is a separate container. On the other hand, you could check if all the keys and values have equal values rather than literally being identical objects using an expression like this:
(set(d1) == set(d2)) and all(d1[k] == d2[k] for k in d1)
The first subexpression makes sure each has keys with the same values and the second that the values associated with each of these keys are equal.
When you reconstitute the dictionary from the saved data a new one is created. It won't be the same dictionary but all its keys and values should be equal to the original. If that is indeed not the case, please provide us with at least the code you're using to determine they are not the same.
the instances of the class that are actually the values of the dictionary looks like:
class SchemaObject:
def __init__(self):
self.type = ''
self.name = ''
self.parentdn = ''
self.dn = ''
self.oclass = ''
def initWithXMLNode(self, xmlnode, parentdn):
self.type = ''
self.name = ''
self.parentdn = parentdn
if xmlnode.nodeName=='fragments':
self.dn = parentdn
if xmlnode.nodeName=='fragment':
self.initFragment(xmlnode)
elif xmlnode.nodeName=='configGroupLdap':
self.initGroup(xmlnode)
elif xmlnode.nodeName=='configObjectLdap':
self.initObject(xmlnode)
def initWithFragment(self, dn, parentdn, name):
self.type = 'object'
self.name = name
self.parentdn = parentdn
self.dn = dn
self.oclass = name
def initFragment(self, xmlnode):
self.type = 'fragment'
self.dn = 'fsFragmentId=' + xmlnode.firstChild.nodeValue + ',' + self.parentdn
self.oclass = 'FSFragment'
def initGroup(self, xmlnode):
self.type = 'group'
self.name = 'group-' + xmlnode.getAttribute('name')
self.dn = xmlnode.getAttribute('dn')
self.oclass = 'FSFragment'
def initObject(self, xmlnode):
self.type = 'object'
self.name = xmlnode.getAttribute('name')
self.oclass = self.name
if not xmlnode.hasAttribute('rdnname'):
self.type = 'no_rdnname'
return
else:
rdnname = xmlnode.getAttribute('rdnname')
parts = rdnname.split(',')
if xmlnode.getAttribute('multiple')!='true':
dn = self.parentdn
for part in reversed(parts):
dn = 'fsFragmentId=' + part + ',' + dn
self.dn = dn
else:
self.type = ''
self.dn = 'fsFragmentId=' + parts[len(parts)-1] + ',' + self.parentdn
dynamicStatics.append(self.oclass)
and in file B.py i create a dictionary that its values are based on this class.
the dict is the my_dict.
and i have also checked that the keys are equal.
only when i try to compare two values between these dictionaries it fails.
so i try to pickle it with:
with open('my_dumped.pkl','wb') as schema:
pickle.dump(my_dict,schema)
and when trying to restore it from disk:
with open('my_dumped.pkl','rb') as schema:
b = pickle.load(schema)
if now i issue my_dumped == b shouldn't this return True?i do not care for identity.just for equality.that as marteanu said the keys are all there,and each key has the correct value.
the above equality returns False unfortunately.