0

Recently I was learning the sequence alignment algorithm. After I got the alignment matrix, I could find an optimal path, but I was in trouble when I was looking for multiple optimal paths (backtracking)!

My idea is to store the results of multiple paths with multiple instances, and finally loop through all instances of the base class to get the answer. I know the following conditions:

  1. What conditions to exit recursion
  2. When do I need to create a new instance and when I don't create it?

But the problem is in the second condition. I don't know how many optimal results there are, and I don't know how many new instances will be created.

So I want to be able to dynamically generate an instance name with a variable.

I don't know how to do this:

# those equivalent to new_instance_name = ResultSeq()
a="new_instance_name"
create_new_instance(a,ResultSeq)

My result base class is ResultSeq:

class KeepRefs(object):
    """
    reference:https://stackoverflow.com/questions/328851/printing-all-instances-of-a-class#comment167339_328851
    """
    __refs__ = defaultdict(list)

    def __init__(self):
        self.__refs__[self.__class__].append(weakref.ref(self))

    @classmethod
    def get_instances(cls):
        for inst_ref in cls.__refs__[cls]:
            inst = inst_ref()
            if inst is not None:
                yield inst


class ResultSeq(KeepRefs):
    """
    save two
    """
    def __init__(self, seq1="", seq2=""):
        super(ResultSeq, self).__init__()
        self.seq1 = seq1
        self.seq2 = seq2

Below is my recursive code:

def multi_backtracking(self, array, i, j, result_seq):
    """
    :param array: V, E, F
    :param i: row
    :param j: col
    :param result_seq: new instance of the class ResultSeq
    :return: Multiple alignment results
    """

    def create_new_obj(name, obj):
        """
        I don't know how to do this.
        """
        pass

    if i == 0 and j == 0:
        pass
    else:
        if array is self.array_V:
            if sum(pass_judgement) == 1:
                """
                An optimal path without creating a new instance.
                """
                self.multi_backtracking(self.array_V, i, j, result_seq)
            else:
                """
                Multiple paths, need to create a new instance
                """
                new_instance_name = "xxx"
                create_new_obj(new_instance_name, ResultSeq)
                ...
                if pass_judgement[0]:
                    result_seq.seq1 = self.origin_seq.seq1[i - 1] + result_seq.seq1
                    result_seq.seq2 = self.origin_seq.seq2[j - 1] + result_seq.seq2
                    self.multi_backtracking(self.array_V, i - 1, j - 1, new_instance_name)
                if pass_judgement[1]:
                    self.multi_backtracking(self.array_E, i, j, new_instance_name)
                if pass_judgement[2]:
                    self.multi_backtracking(self.array_F, i, j, new_instance_name)

This is just one of my solutions. If there are better suggestions, I will be happy to accept them, thank you!

xiaoxu
  • 29
  • 5

1 Answers1

0

You do not need names to store variables - you can use a simple list to store your instances:

class A:
    def __init__(self,value):
        self.value = value
    def __repr__(self):
        return f" _{self.value}_ "

def rec(i):
    """Recursive function, returns a list of instances of class A with decreasing
    value i""" 
    if i < 0:
        return []

    return [A(i)] + rec(i-1) 

k = rec(5)
print(k)

Output:

[ _5_ ,  _4_ ,  _3_ ,  _2_ ,  _1_ ,  _0_ ]

You can acccess your instances inside your list by indexing:

print(k[2])   # _3_

print(k[2].value + k[3].value)  # 5

If you really need names, you can use a dictionary to store them - that is about the same as your existing baseclass KeepRefs does (*):

data = { "Instance1" : A(42), "Instance2" : A(3.141)}

print(data) 
print( data["Instance1"].value + data["Instance2"].value )

Output:

{'Instance1':  _42_ , 'Instance2':  _3.141_ }
45.141

Most of the time when you need user generated "names" for variables you should very strongly reconsider your options.


(*) Your baseclass does not keep non-referenced instances around, a real dict will prevent garbage collecting:

k1 = ResultSeq("A","B")
k2 = ResultSeq("C","D")
k3 = ResultSeq("E","F")

for g in ResultSeq.get_instances():
    print(g.seq1, g.seq2)

k2 = None # no instance of k2 anywhere
k3 = None # no instance of k3 anywhere

for g in ResultSeq.get_instances(): print(g.seq1, g.seq2)

A B
C D
E F

A B # 2.print loop after removing instances k2,k3

Documentation:

https://docs.python.org/3/library/weakref.html
Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
  • I thought that I had to create an instance with a=A() reference. Now I found that A() can be created which is no reference. If we use **weakref**, it will be recycled without reference. I understand now, I will give up using it in this script and use a list or dictionary to store the instance. Thank you!! – xiaoxu Jun 02 '19 at 10:47
  • Sorry to bother you, I used a list to store the instance, I think he didn't work when destroying the instance, I want to ask why? `class ResultSeq(object): _all_instances = [] def __init__(self, seq=""): self._all_instances.append(self) def __del__(self): self._all_instances.remove(self) a = ResultSeq("a") b = ResultSeq("b") c = ResultSeq("c") del a print(ResultSeq._all_instances)` – xiaoxu Jun 02 '19 at 11:16
  • @xiaoxu Read [about `__del__`](https://stackoverflow.com/questions/1481488/what-is-the-del-method-how-to-call-it) - garbage collection happens when it happens - not needingly as soon as you call `del a` - you can use `__del__` directly: `a.__del__() ` – Patrick Artner Jun 02 '19 at 12:00