类中的可订阅对象

时间:2015-11-16 01:27:11

标签: python class python-3.x subscript

我有一个名为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)

Functions

Class Definition

Driver

2 个答案:

答案 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的一些观点:

  1. 当您指定SomeClass作为元类时,将调用SomeClass来创建Dog类。

  2. Dog类将是元类的__new__()方法的返回值。

  3. 元类中的其他方法不会被继承。

  4. 以下是一个例子:

    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__()方法可用于初始化