我有一个名为Python 3.4.3
的{{1}}类,用于表示稀疏矩阵和它们之间的操作。
除了这个类,我还有函数定义,包括如何重载加法运算,打印函数等。但是,当稀疏矩阵表示为字典时,这些函数被写入,其中键表示非零条目的位置和相应的非-zero条目作为值。
这个函数在这个先前的实现中运行良好,但是当稀疏矩阵被实现为类时,这种方法不能产生
Sparse
我如何规避这个问题?我附上相关代码以供参考。 错误追溯是
TypeError: ` 'Sparse' object is not subscriptable`.
功能定义
Traceback (most recent call last):
File "sparse_driver.py", line 11, in <module>
print(s)
File "/mnt/DATA/Dropbox/Courses/Python/HW4/SparseClass.py", line 42, in __str__
sparse_functions.sparse_print(self)
File "/mnt/DATA/Dropbox/Courses/Python/HW4/sparse_functions.py", line 56, in sparse_print
nrow = a['-1'][0]
TypeError: 'Sparse' object is not subscriptable
课程定义
import random
# Deepcopy is imported in order not to change the matrix a itself
from copy import deepcopy
def sparse_gen(num_row, num_col, spar):
# Total number of elements
nelem = num_row * num_col
# Number of nonzero elements
n_nonzero = int(nelem * spar)
# Position of nonzero elements
poselem = random.sample(range(nelem), n_nonzero)
# Create an empty dictionary here
spardict = {};
# Add entries into the dictionary here in a for loop
for i in range(len(poselem)):
# Create a string from the number in poselem and use it as a key
key = str(poselem[i])
# Select -1 or 1 randomly
spardict[key] = random.choice([-1,1])
# Add the size of the matrix with the key value -1
spardict['-1'] = [num_row, num_col]
# Return the sparse matrix for reference later on
return spardict
# Here is the function for addition of two matrices
def sparse_add(a, b):
# First lets check if the matrix sizes are equal or not
if a['-1'] != b['-1'] :
print('Matrix sizes should be equal. Exiting!')
return
# Copy matrix a into matrix c the sum a + b = c
c = deepcopy(a)
# Delete the size info from b and retrieve the keys
del b['-1']
bkeys = b.keys()
# Write the for loop for summation iterating over keys of b
for bkey in iter(bkeys):
if (bkey in a):
if ((c[bkey] + b[bkey]) == 0):
del c[bkey]
continue
else:
c[bkey] += b[bkey]
else:
c[bkey] = b[bkey]
# Since it gives rise to problems later readd b['-1']
b['-1'] = a['-1']
return c
# The function for printing sparse matrices is defined here
def sparse_print(a):
# Lets retrieve the size information first (rows, columns)
# Remember the size information is a list
nrow = a['-1'][0]
ncol = a['-1'][1]
# Lets write a for loop to print out the relevant info
for i in range(nrow * ncol):
# if the i is in a print that value otherwise print 0
return_str = ""
if str(i) in a:
return_str += "%3d " % a[str(i)]
else:
return_str += "%3d " % 0
# Print new line if we hit the end of the row
if ((i + 1) % nrow) == 0:
return_str += "\n"
return_str += "\n\n"# Two new line characters to avoid confusion
return return_str
# The function for converting sparse matrices to full is defined here
def mat2sparse(a):
spardict = {}
num_row = len(a)
num_col = len(a[0])
nelem = num_row * num_col
# Input the size information
spardict['-1'] = [num_row, num_col]
for i in range(num_row):
for j in range(num_col):
if a[i][j] != 0:
# Calculate the position of the key
key = str(i*num_col + j)
spardict[key] = a[i][j]
return spardict
驱动程序
import sparse_functions
class GetAttr(type):
def __getitem__(cls, x):
return getattr(cls, x)
class Sparse:
__metaclass__ = GetAttr
""" Class for sparse matrices"""
# This is the constructor when dimensions and sparsity are specified
def __init__(self, *args):
# The name of the helper function is sparse_gen
if len(args) == 3:
num_row = args[0]
num_col = args[1]
spar = args[2]
if ((type(args[0]) is not int) or (type(args[1]) is not int)):
raise TypeError('The first two arguments should be integers!')
elif not ((args[0] > 0) and (args[1] > 0)):
raise ValueError('The first two agruments should be positive!')
elif not ((args[2] < 1) and (args[2] > 0)):
raise ValueError('Sparsity must be between 0 and 1!')
self.sparse_rep = sparse_functions.sparse_gen(num_row, num_col, spar)
elif len(args) == 1:
if (type(args[0] is not list)):
raise TypeError('The only argument supplied should be a list!')
# The list of lists matrix is provided convert it to sparse
self.sparse_rep = sparse_functions.mat2sparse(arg[0])
else:
raise AttributeError('Invalid number of arguments. There should be either one argument or three arguments!')
# Overload the addition operation
def __add__(a,b):
# Here we can make use of the already defined functions
return sparse_functions.sparse_add(a,b)
# Overload the subtraction operator
def __sub__(a,b):
return sparse_functions.sparse_add(a,-b)
# Overload the print function
def __str__(self):
sparse_functions.sparse_print(self)
答案 0 :(得分:2)
这里的问题是您将元类(类他们的实例)与超级类的使用混淆不清(类是他们的子类)。
Meta Classes对类起作用,表示它们定义的方法(如__getitem__
)具有一个参数,我们通常将其表示为一个类:__getitem__(cls, index)
。
在用户定义的类上分配自定义元类时,用户定义的类的实例不会使用自定义元类中定义的方法,而是使用它们的类。
这与超级类形成对比,超级类我们继承方法,适用于用户定义类的实例。
如果没有实施你想要做的事情,我会举一个小例子来证明这一点(使用Py3.5),我希望你能得到它的要点。
首先,定义__getitem__()
的基础元类:
class MetaCls(type):
def __getitem__(cls, index):
print("Using meta __getitem__ on classes that have my type"
如果我们将__metaclass__ = MetaCls
类定义为使用Base
,则会对所有具有MetaCls
的类执行,我们可以看到它适用于{{ 1}} 但不是这些类的实例:
classes
为了使订阅也适用于实例,我们需要定义适当的# metaclass is defined in header:
class Base(metaclass=MetaCls):
pass
# works on classes
Base[0]
Using meta __getitem__ on classes that have my type
base_instance = Base()
# fails on instances
base_instance[0] # TypeError!
,我们可以在其中创建子类,或者在类Super Class
中重载我们的__getitem__
方法:
Base
现在,class SuperBase:
def __getitem__(self, index):
print("Using SuperBase __getitem__ on instances that derive from classes that subclass me")
class Base(SuperBase, metaclass=MetaCls):
pass
适用于和类,对于使用__getitem__
的类,对于使用MetaCls.__getitem__(cls, index)
的实例:
SuperBase.__getitem__(self, index)
最后,不确定为什么你在这种情况下考虑定义自定义元,因为超级类看起来更合适,但我希望你有理由。
无论哪种方式,即使您使用重载Base[0] # meta invocation
Using meta __getitem__ on classes that have my type
base_instance = Base()
base_instance[0] # super invocation
Using SuperBase __getitem__ on instances that derive from classes that subclass me
定义超类,这实际上会导致另一个错误:
保留剩下的部分,只需添加超类而不是元类:
__getitem__
这是有道理的; print(d) # Raises the following error:
1 class GetAttr:
2 def __getitem__(self, index):
----> 3 return getattr(self, index)
4 class Sparse(GetAttr):
5 """ Class for sparse matrices"""
AttributeError: 'Sparse' object has no attribute '-1'
会调用print(d)
来调用nrow = a['-1'][0]
__getitem__
和Sparce
的实例。
index= -1
,因为它已编程,然后会尝试从您的班级__getitem__
中获取名为-1
的属性,这显然不存在。
我建议可能在Sparce
类中重载__getitem__
,在 Sparce
内重载对您实例中类似数组的对象执行一些索引操作。 (可能是__getitem__
?)
答案 1 :(得分:1)
...这种方法无法产生&#39; Sparse&#39;的TypeError。对象不是 标化。我该如何规避这个问题呢?
使用inheritance
:
class GetAttr:
def __getitem__(cls, x):
return getattr(cls, x)
class Sparse(GetAttr):
x = 10
s = Sparse()
print(s['x'])
--output:--
10
顺便提一下,在python3.4
__metaclass__
行:
class Dog:
__metaclass__ = SomeClass
什么都不做。我必须写:
class Dog(metaclass=SomeClass):
有关metaclasses
的一些观点:
当您指定SomeClass作为元类时,将调用SomeClass来创建Dog类。
Dog类将是元类的__new__()
方法的返回值。
元类中的其他方法不会被继承。
以下是一个例子:
class MyMetaClass(type):
def stop(self):
print('I will stop.')
def __new__(cls, name, parents, dct):
dct["start"] = lambda self: print('I will start\n.') #See explanation below
return super().__new__(cls, name, parents, dct)
class Dog(metaclass=MyMetaClass):
pass
d = Dog()
d.start()
d.stop()
--output:--
I will start.
Traceback (most recent call last):
File "1.py", line 14, in <module>
d.stop()
AttributeError: 'Dog' object has no attribute 'stop'
请注意,在Python中,对象实现为dictionaries
,这意味着实例具有存储变量的字典,并且类具有存储方法和变量的字典。在上面的代码中,dct
将是新创建的类的类字典,在__new__()
内,您可以将事物存储在类字典中,然后它们将在新类中可用。
上面的例子提出了对你的元类执行此操作的可能性:
class MyMetaClass(type):
def __getitem__(cls, x):
return getattr(cls, x)
def __new__(cls, name, parents, dct):
dct["__getitem__"] = cls.__getitem__ #<*****HERE
return super().__new__(cls, name, parents, dct)
class Dog(metaclass=MyMetaClass):
x = 10
d = Dog()
print(d['x'])
--output:--
10
类__init__()
方法可用于初始化类的实例,类似地,元类的__new__()
方法可用于初始化类