开发者

python: iterate using a class method

I'm still digging in python, and there are a few things that my C++ background is getting in the way. For example, I have a class

class crack(object):

   def __init__(self, sz1,sz2):
        self.z1 = sz1
        self.z2 = sz2

   def smallz_Z(self,z,z1,z2):
       return z - 0.5*(z2-z1)

   def get_pot(self,z):
        Z = smallz_Z(z,self.z1,self.z2)

        try:
             result = np.sum(np.arange(self.n) * self.coeffs * (1.0 / (cmath.sqrt(Z - 1.0) * cmath.sqrt(Z + 1.0))) 
                        * ((Z - cmath.sqrt(Z - 1.0) * cmath.sqrt(Z + 1.0)) ** np.arange(self.n))) * (2.0 / (self.z2 - self.z1))

I'm going to create a list for crack elements,

crack_list = []
crack_list.append(crack(z1,z2)) ...

And now I n开发者_StackOverfloweed to use a iterator to get all the values of potential, for example for the matplotlib contour function. I can't do things like this:

result = np.sum(crack_list.get_potential(z))

Right now I'm doing the 'traditional' way:

def potential_value(z, crack_list):
     potential = complex(0, 0)

     for element in crack_list:
     potential = potential + element.get_potential(z)

    return potential

and if I use a for loop, it says that the element crack is not iterable. Let's say that I try to

    x = np.linspace(self.x1, self.x2, self.step)
    y = np.linspace(self.y1, self.y2, self.step)
    X, Y = np.meshgrid(x, y)
    Z = X +1j*Y

    F = np.vectorize(potential_value)

but here I have a problem:

   F(Z, crack_list)

   for element in crack_list:
      TypeError: 'crack' object is not iterable

How can I do that? Thanks.

EDIT: Thanks for the answers. I'm still getting to know list comprehension, so it's still beating me. Let's say that I want to change my implementation for list comprehension, so I change the function to

def potential_value(z_list, crack_list, u_flow):

return [np.sum([c.get_potential(z) for c in crack_list]) + u_flow.get_potential_no_c(z) for z in z_list]

Where I'm trying to use now two list comprehensions at the same time, so the potential would return an array with all the results for all the z_list variables - noting that z_list should be a list for complex types. Is there a way to do that for just one complex value in z_list, without having to pass a list with just one value? Or I have to do some kind of test?


Object to be iterable needs to have __iter__() method, which returns an iterator (or sequence). Iterator should implement method next(), which will yield elements or raise StopIteration is there are no more elements. As simple as that.


In addition to the get_pot() method not returning anything, it looks like you don't define self.n or self.coeffs anywhere. I'm betting coeffs is meant to be a numpy vector, and n its length, and I'll assume they could vary between Crack objects just like z1 and z2.

Based on those assumptions, I tweaked your class a little:

class Crack(object): # convention is to use capitalized class names

    def __init__(self, z1, z2, coeffs):
        self.z1 = z1
        self.z2 = z2
        self.coeffs = np.array(coeffs)
        self.n = len(coeffs)

    def __repr__(self):
        return "Crack(z1=%s, z2=%s, coeffs=%s)" % (self.z1, 
                                                   self.z2, 
                                                   self.coeffs)
    def get_big_Z(self, little_z):
        # this may not need to be its own function, unless you
        # use it separately from get_potential()
        return little_z - 0.5 * (self.z2 - self.z1)

    def get_potential(self, z):
        Z = self.get_big_Z(z)
        return (np.sum(np.arange(self.n) * self.coeffs * 
                       (1.0 / (np.sqrt(Z - 1.0) * np.sqrt(Z + 1.0))) * 
                       ((Z - np.sqrt(Z - 1.0) * np.sqrt(Z + 1.0)) ** 
                       np.arange(self.n))) * 
                (2.0 / (self.z2 - self.z1))
                )

Now, suppose you have (or can generate) a list of the z1, z2, and coeffs values for each Crack:

>>> # dummy information, this makes three Crack objects
...
>>> z1_values = [3.0, 1.1, 0.2]
>>> z2_values = [0.01, 0.02, 0.03]
>>> coeff_values = [[1.1,1.0],
...                 [2.2,2.0],
...                 [3.3,3.0]]

The coeff lists are converted to ndarray when the object is instantiated.

You can make crack_list with the built-in function map() like this:

>>> crack_list = map(Crack, z1_values, z2_values, coeff_values)
>>> from pprint import pprint
>>> pprint(crack_list)
[Crack(z1=3.0, z2=0.01, coeffs=[ 1.1  1. ]),
 Crack(z1=1.1, z2=0.02, coeffs=[ 2.2  2. ]),
 Crack(z1=0.2, z2=0.03, coeffs=[ 3.3  3. ])]

Then you can compute the total potential for some z values, say z = 5.0 or z = 3.14 like:

>>> def total_potential(z, cracks):
...     return sum(c.get_potential(z) for c in cracks)
...
>>>
>>> print total_potential(5.0, crack_list)
-0.772861759407
>>> print total_potential(3.14, crack_list)
-1.99073949493

Voila. As a check, I'd also do some pen-and-paper calculations just to be sure that big equation is doing what it should be doing.


You can create a list of potentials with a list comprehension, replacing

result = np.sum(crack_list.get_potential(z))

with

result = np.sum([elem.get_potential(z) for elem in crack_list])

Map and reduce can also be used in this context.


First, I don't see that your get_pot method returns anything. It should end with return result i think.

Second, stop-gap measure would be to iterate over your list of objects:

[e.get_pot(i) for i in range(10)]

But ideally, since you are using numpy, you should get your class to accept arrays (which it looks like it might already). See what happens when you input sz1, sz2, and z as equal-shape 2D arrays. The, if you sum over just one axis, get_pot should then return an iterable array. This would avoid creating lists of instances and take advantage of speedups that numpy offers. (The effectiveness of this solution is a bit dependent on the number of crack instances you have and how often you create and destroy new instances)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜