class 中的可订阅 objects
Subscriptable objects in class
我有一个名为 Sparse
的 Python 3.4.3
class 来表示稀疏矩阵和它们之间的运算。
连同这个 class 我有函数定义,包括如何重载加法运算、打印函数等。但是,这些函数是在稀疏矩阵表示为字典时编写的,键表示 non-zero 条目和相应的 non-zero 条目作为值。
这些函数在之前的实现中运行良好,但是当稀疏矩阵作为 class 实现时,这种方法无法产生
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
Class定义
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)
Driver
import random
from SparseClass import *
s=Sparse(4,4,0.2)
p=Sparse(4,4,0.3)
# Both of the following lines are problematic
print(s)
s + p
...this approach fails yielding a TypeError of 'Sparse' object is not
subscriptable. How can I circumvent this issue?
通过使用 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 指定为 metaclass 时,将调用 SomeClass 来创建 Dog class。
狗 class 将是元class 的 __new__()
方法的 return 值。
metaclass中的其他方法不被继承
这是一个例子:
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
,这意味着一个实例有一个字典,其中存储了变量,而一个 class 有一个字典,其中有方法和变量存储。在上面的代码中,dct
将是新创建的 class 的 class 字典,而在 __new__()
中,您可以将内容存储在 class 字典中,然后它们将在新 class.
中提供
上面的例子增加了对你的 meta 做这个的可能性class:
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
class 的 __init__()
方法可用于初始化 class 的实例,类似地,metaclass 的 __new__()
方法可以用来初始化一个class.
这里的问题是你混淆了 Meta Classes 的使用(classes 是 它们的实例) 和 Super Classes (classes 是他们的 sub-classes).
Meta Classes 作用于 classes,表明它们定义的方法(如 __getitem__
)有一个参数,我们通常将其表示为 class 来自:__getitem__(cls, index)
.
在用户定义 class 上分配自定义元 class 时,用户定义 class 的实例将不会使用自定义元 [=90] 中定义的方法=],他们的 classes 会。
这与 Super Classes 形成对比,我们从中 继承 适用于用户定义 class.[=41 实例的方法=]
在不实施你正在尝试做的事情的情况下,我将举一个小例子来证明这一点(使用 Py3.5),我希望你能理解它的要点。
首先,基础元class定义__getitem__()
:
class MetaCls(type):
def __getitem__(cls, index):
print("Using meta __getitem__ on classes that have my type"
如果我们将 Base
Class 定义为使用MetaCls
我们可以看到这适用于 classes
但不适用于 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
,我们可以从中子class 或者,在 [=90 中重载我们的 __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__
适用于 classes 和 实例,对于 classes 它使用 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
最后,不确定您为什么考虑在这种情况下定义自定义元,因为 Super Class 似乎更合适,但我希望您有自己的理由。
无论哪种方式,即使您确实定义了带有重载 __getitem__
的超级 Class,这也确实会导致另一个错误:
保持你粘贴的其余部分,只添加一个 superclass 而不是 metaclass:
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]
,后者将使用 Sparce
和 index= -1
.
的实例调用 __getitem__
__getitem__
,因为它的程序将尝试从你的 class Sparce
中获取名为 -1
的属性,这显然不存在。
我建议在 Sparce
class 和 内部 __getitem__
中重载 __getitem__
执行一些索引操作您的实例中的类数组对象。 (可能 self.sparse_rep
?)
我有一个名为 Sparse
的 Python 3.4.3
class 来表示稀疏矩阵和它们之间的运算。
连同这个 class 我有函数定义,包括如何重载加法运算、打印函数等。但是,这些函数是在稀疏矩阵表示为字典时编写的,键表示 non-zero 条目和相应的 non-zero 条目作为值。
这些函数在之前的实现中运行良好,但是当稀疏矩阵作为 class 实现时,这种方法无法产生
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
Class定义
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)
Driver
import random
from SparseClass import *
s=Sparse(4,4,0.2)
p=Sparse(4,4,0.3)
# Both of the following lines are problematic
print(s)
s + p
...this approach fails yielding a TypeError of 'Sparse' object is not subscriptable. How can I circumvent this issue?
通过使用 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 指定为 metaclass 时,将调用 SomeClass 来创建 Dog class。
狗 class 将是元class 的
__new__()
方法的 return 值。metaclass中的其他方法不被继承
这是一个例子:
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
,这意味着一个实例有一个字典,其中存储了变量,而一个 class 有一个字典,其中有方法和变量存储。在上面的代码中,dct
将是新创建的 class 的 class 字典,而在 __new__()
中,您可以将内容存储在 class 字典中,然后它们将在新 class.
上面的例子增加了对你的 meta 做这个的可能性class:
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
class 的 __init__()
方法可用于初始化 class 的实例,类似地,metaclass 的 __new__()
方法可以用来初始化一个class.
这里的问题是你混淆了 Meta Classes 的使用(classes 是 它们的实例) 和 Super Classes (classes 是他们的 sub-classes).
Meta Classes 作用于 classes,表明它们定义的方法(如 __getitem__
)有一个参数,我们通常将其表示为 class 来自:__getitem__(cls, index)
.
在用户定义 class 上分配自定义元 class 时,用户定义 class 的实例将不会使用自定义元 [=90] 中定义的方法=],他们的 classes 会。
这与 Super Classes 形成对比,我们从中 继承 适用于用户定义 class.[=41 实例的方法=]
在不实施你正在尝试做的事情的情况下,我将举一个小例子来证明这一点(使用 Py3.5),我希望你能理解它的要点。
首先,基础元class定义__getitem__()
:
class MetaCls(type):
def __getitem__(cls, index):
print("Using meta __getitem__ on classes that have my type"
如果我们将 Base
Class 定义为使用MetaCls
我们可以看到这适用于 classes
但不适用于 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
,我们可以从中子class 或者,在 [=90 中重载我们的 __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__
适用于 classes 和 实例,对于 classes 它使用 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
最后,不确定您为什么考虑在这种情况下定义自定义元,因为 Super Class 似乎更合适,但我希望您有自己的理由。
无论哪种方式,即使您确实定义了带有重载 __getitem__
的超级 Class,这也确实会导致另一个错误:
保持你粘贴的其余部分,只添加一个 superclass 而不是 metaclass:
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]
,后者将使用 Sparce
和 index= -1
.
__getitem__
__getitem__
,因为它的程序将尝试从你的 class Sparce
中获取名为 -1
的属性,这显然不存在。
我建议在 Sparce
class 和 内部 __getitem__
中重载 __getitem__
执行一些索引操作您的实例中的类数组对象。 (可能 self.sparse_rep
?)