所有由Python实用宝典发布的文章

Python datetime-在使用strptime获取日,月,年之后设置固定的小时和分钟

问题:Python datetime-在使用strptime获取日,月,年之后设置固定的小时和分钟

我已经成功地将26 Sep 2012格式转换为26-09-2012使用:

datetime.strptime(request.POST['sample_date'],'%d %b %Y')

但是,我不知道如何将上述时间设置为11:59。有谁知道如何做到这一点?

请注意,这可以是将来的日期,也可以是任何随机的日期,而不仅仅是当前日期。

I’ve successfully converted something of 26 Sep 2012 format to 26-09-2012 using:

datetime.strptime(request.POST['sample_date'],'%d %b %Y')

However, I don’t know how to set the hour and minute of something like the above to 11:59. Does anyone know how to do this?

Note, this can be a future date or any random one, not just the current date.


回答 0

用途datetime.replace

from datetime import datetime
date = datetime.strptime('26 Sep 2012', '%d %b %Y')
newdate = date.replace(hour=11, minute=59)

Use datetime.replace:

from datetime import datetime
date = datetime.strptime('26 Sep 2012', '%d %b %Y')
newdate = date.replace(hour=11, minute=59)

回答 1

datetime.replace()将提供最佳选择。此外,它还提供了替换日,年和月的工具。

假设我们有一个datetime对象,日期表示为: "2017-05-04"

>>> from datetime import datetime
>>> date = datetime.strptime('2017-05-04',"%Y-%m-%d")
>>> print(date)
2017-05-04 00:00:00
>>> date = date.replace(minute=59, hour=23, second=59, year=2018, month=6, day=1)
>>> print(date)
2018-06-01 23:59:59

datetime.replace() will provide the best options. Also, it provides facility for replacing day, year, and month.

Suppose we have a datetime object and date is represented as: "2017-05-04"

>>> from datetime import datetime
>>> date = datetime.strptime('2017-05-04',"%Y-%m-%d")
>>> print(date)
2017-05-04 00:00:00
>>> date = date.replace(minute=59, hour=23, second=59, year=2018, month=6, day=1)
>>> print(date)
2018-06-01 23:59:59

Matplotlib(pyplot)savefig输出空白图像

问题:Matplotlib(pyplot)savefig输出空白图像

我正在尝试保存使用matplotlib创建的图;但是,图像保存为空白。

这是我的代码:

plt.subplot(121)
plt.imshow(dataStack, cmap=mpl.cm.bone)

plt.subplot(122)
y = copy.deepcopy(tumorStack)
y = np.ma.masked_where(y == 0, y)

plt.imshow(dataStack, cmap=mpl.cm.bone)
plt.imshow(y, cmap=mpl.cm.jet_r, interpolation='nearest')

if T0 is not None:
    plt.subplot(123)
    plt.imshow(T0, cmap=mpl.cm.bone)

    #plt.subplot(124)
    #Autozoom

#else:
    #plt.subplot(124)
    #Autozoom

plt.show()
plt.draw()
plt.savefig('tessstttyyy.png', dpi=100)

tessstttyyy.png为空白(也尝试使用.jpg)

I am trying to save plots I make using matplotlib; however, the images are saving blank.

Here is my code:

plt.subplot(121)
plt.imshow(dataStack, cmap=mpl.cm.bone)

plt.subplot(122)
y = copy.deepcopy(tumorStack)
y = np.ma.masked_where(y == 0, y)

plt.imshow(dataStack, cmap=mpl.cm.bone)
plt.imshow(y, cmap=mpl.cm.jet_r, interpolation='nearest')

if T0 is not None:
    plt.subplot(123)
    plt.imshow(T0, cmap=mpl.cm.bone)

    #plt.subplot(124)
    #Autozoom

#else:
    #plt.subplot(124)
    #Autozoom

plt.show()
plt.draw()
plt.savefig('tessstttyyy.png', dpi=100)

And tessstttyyy.png is blank (also tried with .jpg)


回答 0

首先,什么时候会发生T0 is not None?我会测试一下,然后再调整传递给的值plt.subplot();可以尝试使用值131、132和133,或者取决于是否T0存在的值。

其次,在plt.show()调用之后,创建一个新图形。为了解决这个问题,您可以

  1. 调用plt.savefig('tessstttyyy.png', dpi=100)之前调用plt.show()

  2. show()通过调用plt.gcf()“获取当前图形”来保存图形,然后可以随时调用savefig()Figure对象。

例如:

fig1 = plt.gcf()
plt.show()
plt.draw()
fig1.savefig('tessstttyyy.png', dpi=100)

在您的代码中,“ tesssttyyy.png”为空白,因为它保存的是新图形,该图形上没有任何内容。

First, what happens when T0 is not None? I would test that, then I would adjust the values I pass to plt.subplot(); maybe try values 131, 132, and 133, or values that depend whether or not T0 exists.

Second, after plt.show() is called, a new figure is created. To deal with this, you can

  1. Call plt.savefig('tessstttyyy.png', dpi=100) before you call plt.show()

  2. Save the figure before you show() by calling plt.gcf() for “get current figure”, then you can call savefig() on this Figure object at any time.

For example:

fig1 = plt.gcf()
plt.show()
plt.draw()
fig1.savefig('tessstttyyy.png', dpi=100)

In your code, ‘tesssttyyy.png’ is blank because it is saving the new figure, to which nothing has been plotted.


回答 1

plt.show() 应该来 plt.savefig()

说明:plt.show()清除所有内容,因此以后任何事情都会在一个新的空白图形上发生

plt.show() should come after plt.savefig()

Explanation: plt.show() clears the whole thing, so anything afterwards will happen on a new empty figure


回答 2

更改功能的顺序为我解决了问题

  • 首先 保存情节
  • 然后 显示剧情

如下:

plt.savefig('heatmap.png')

plt.show()

change the order of the functions fixed the problem for me:

  • first Save the plot
  • then Show the plot

as following:

plt.savefig('heatmap.png')

plt.show()

回答 3

在show()对我有用之前调用savefig。

fig ,ax = plt.subplots(figsize = (4,4))
sns.barplot(x='sex', y='tip', color='g', ax=ax,data=tips)
sns.barplot(x='sex', y='tip', color='b', ax=ax,data=tips)
ax.legend(['Male','Female'], facecolor='w')

plt.savefig('figure.png')
plt.show()

Calling savefig before show() worked for me.

fig ,ax = plt.subplots(figsize = (4,4))
sns.barplot(x='sex', y='tip', color='g', ax=ax,data=tips)
sns.barplot(x='sex', y='tip', color='b', ax=ax,data=tips)
ax.legend(['Male','Female'], facecolor='w')

plt.savefig('figure.png')
plt.show()

回答 4

让我给一个更详细的例子:

import numpy as np
import matplotlib.pyplot as plt


def draw_result(lst_iter, lst_loss, lst_acc, title):
    plt.plot(lst_iter, lst_loss, '-b', label='loss')
    plt.plot(lst_iter, lst_acc, '-r', label='accuracy')

    plt.xlabel("n iteration")
    plt.legend(loc='upper left')
    plt.title(title)
    plt.savefig(title+".png")  # should before plt.show method

    plt.show()


def test_draw():
    lst_iter = range(100)
    lst_loss = [0.01 * i + 0.01 * i ** 2 for i in xrange(100)]
    # lst_loss = np.random.randn(1, 100).reshape((100, ))
    lst_acc = [0.01 * i - 0.01 * i ** 2 for i in xrange(100)]
    # lst_acc = np.random.randn(1, 100).reshape((100, ))
    draw_result(lst_iter, lst_loss, lst_acc, "sgd_method")


if __name__ == '__main__':
    test_draw()

let’s me give a more detail example:

import numpy as np
import matplotlib.pyplot as plt


def draw_result(lst_iter, lst_loss, lst_acc, title):
    plt.plot(lst_iter, lst_loss, '-b', label='loss')
    plt.plot(lst_iter, lst_acc, '-r', label='accuracy')

    plt.xlabel("n iteration")
    plt.legend(loc='upper left')
    plt.title(title)
    plt.savefig(title+".png")  # should before plt.show method

    plt.show()


def test_draw():
    lst_iter = range(100)
    lst_loss = [0.01 * i + 0.01 * i ** 2 for i in xrange(100)]
    # lst_loss = np.random.randn(1, 100).reshape((100, ))
    lst_acc = [0.01 * i - 0.01 * i ** 2 for i in xrange(100)]
    # lst_acc = np.random.randn(1, 100).reshape((100, ))
    draw_result(lst_iter, lst_loss, lst_acc, "sgd_method")


if __name__ == '__main__':
    test_draw()


用多重继承调用父类__init__,正确的方法是什么?

问题:用多重继承调用父类__init__,正确的方法是什么?

假设我有多个继承方案:

class A(object):
    # code for A here

class B(object):
    # code for B here

class C(A, B):
    def __init__(self):
        # What's the right code to write here to ensure 
        # A.__init__ and B.__init__ get called?

有编写的两个典型方法C__init__

  1. (老式) ParentClass.__init__(self)
  2. (较新的样式) super(DerivedClass, self).__init__()

但是,在任何一种情况下,如果父类(AB没有遵循相同的约定,则代码将无法正常工作(某些代码可能会丢失或多次调用)。

那么又是什么正确的方法呢?说“保持一致,遵循一个或另一个”很容易,但是如果AB来自第三方图书馆,那又如何呢?有没有一种方法可以确保所有父类构造函数都被调用(以正确的顺序,并且只能调用一次)?

编辑:看看我的意思,如果我这样做:

class A(object):
    def __init__(self):
        print("Entering A")
        super(A, self).__init__()
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        A.__init__(self)
        B.__init__(self)
        print("Leaving C")

然后我得到:

Entering C
Entering A
Entering B
Leaving B
Leaving A
Entering B
Leaving B
Leaving C

请注意,Binit会被调用两次。如果我做:

class A(object):
    def __init__(self):
        print("Entering A")
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        super(C, self).__init__()
        print("Leaving C")

然后我得到:

Entering C
Entering A
Leaving A
Leaving C

请注意,B永远不会调用init。因此,似乎除非我知道/控制我从(AB)继承的类的初始化,否则我无法对正在编写的类(C)做出安全选择。

Say I have a multiple inheritance scenario:

class A(object):
    # code for A here

class B(object):
    # code for B here

class C(A, B):
    def __init__(self):
        # What's the right code to write here to ensure 
        # A.__init__ and B.__init__ get called?

There’s two typical approaches to writing C‘s __init__:

  1. (old-style) ParentClass.__init__(self)
  2. (newer-style) super(DerivedClass, self).__init__()

However, in either case, if the parent classes (A and B) don’t follow the same convention, then the code will not work correctly (some may be missed, or get called multiple times).

So what’s the correct way again? It’s easy to say “just be consistent, follow one or the other”, but if A or B are from a 3rd party library, what then? Is there an approach that can ensure that all parent class constructors get called (and in the correct order, and only once)?

Edit: to see what I mean, if I do:

class A(object):
    def __init__(self):
        print("Entering A")
        super(A, self).__init__()
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        A.__init__(self)
        B.__init__(self)
        print("Leaving C")

Then I get:

Entering C
Entering A
Entering B
Leaving B
Leaving A
Entering B
Leaving B
Leaving C

Note that B‘s init gets called twice. If I do:

class A(object):
    def __init__(self):
        print("Entering A")
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        super(C, self).__init__()
        print("Leaving C")

Then I get:

Entering C
Entering A
Leaving A
Leaving C

Note that B‘s init never gets called. So it seems that unless I know/control the init’s of the classes I inherit from (A and B) I cannot make a safe choice for the class I’m writing (C).


回答 0

两种方式都可以正常工作。使用该方法super()可为子类带来更大的灵活性。

在直接呼叫方式中,C.__init__可以同时呼叫A.__init__B.__init__

使用时super(),需要将类设计为在其中C调用的协作式多重继承super,这将调用A的代码,该代码还将super调用B的代码。请参阅http://rhettinger.wordpress.com/2011/05/26/super-considered-super,以详细了解可以使用进行的操作super

[回答问题,稍后编辑]

因此,似乎除非我知道/控制我从(A和B)继承的类的初始化,否则我无法对我正在编写的类(C)做出安全的选择。

参考的文章显示了如何通过在A和周围添加包装器类来处理这种情况B。标题为“如何合并非合作类”的部分提供了一个可行的示例。

可能希望多重继承更容易,让您轻松组成Car和Airplane类来获得FlyingCar,但现实情况是,单独设计的组件通常需要适配器或包装器,然后才能像我们希望的那样无缝地组装在一起:-)

另一个想法:如果您对使用多重继承来编写功能不满意,则可以使用composition来完全控制在哪些情况下调用哪种方法。

Both ways work fine. The approach using super() leads to greater flexibility for subclasses.

In the direct call approach, C.__init__ can call both A.__init__ and B.__init__.

When using super(), the classes need to be designed for cooperative multiple inheritance where C calls super, which invokes A‘s code which will also call super which invokes B‘s code. See http://rhettinger.wordpress.com/2011/05/26/super-considered-super for more detail on what can be done with super.

[Response question as later edited]

So it seems that unless I know/control the init’s of the classes I inherit from (A and B) I cannot make a safe choice for the class I’m writing (C).

The referenced article shows how to handle this situation by adding a wrapper class around A and B. There is a worked-out example in the section titled “How to Incorporate a Non-cooperative Class”.

One might wish that multiple inheritance were easier, letting you effortlessly compose Car and Airplane classes to get a FlyingCar, but the reality is that separately designed components often need adapters or wrappers before fitting together as seamlessly as we would like :-)

One other thought: if you’re unhappy with composing functionality using multiple inheritance, you can use composition for complete control over which methods get called on which occasions.


回答 1

您问题的答案取决于一个非常重要的方面:您的基类是否设计用于多重继承?

有3种不同的方案:

  1. 基类是不相关的独立类。

    如果您的基类是能够独立运行的独立实体,并且彼此之间不认识,则它们不是为多重继承设计的。例:

    class Foo:
        def __init__(self):
            self.foo = 'foo'
    
    class Bar:
        def __init__(self, bar):
            self.bar = bar
    

    重要:请注意,既不打电话Foo也不Bar打电话super().__init__()!这就是为什么您的代码无法正常工作的原因。由于Diamond继承在python中的工作方式,因此object不应调用基类为的类super().__init__()。如您所知,这样做会破坏多重继承,因为您最终将调用另一个类的__init__而不是object.__init__()免责声明:避免super().__init__()object-subclasses中是我个人的建议,绝不是python社区中达成一致的共识。有些人更喜欢super在每个类中使用,认为如果该类的行为不像您通常可以编写一个适配器您期望的。)

    这也意味着您永远不应编写从其继承object且没有__init__方法的类。完全不定义__init__方法与调用具有相同的效果super().__init__()。如果您的类直接继承自object,请确保添加一个空的构造函数,如下所示:

    class Base(object):
        def __init__(self):
            pass
    

    无论如何,在这种情况下,您将必须手动调用每个父构造函数。有两种方法可以做到这一点:

    • 不带 super

      class FooBar(Foo, Bar):
          def __init__(self, bar='bar'):
              Foo.__init__(self)  # explicit calls without super
              Bar.__init__(self, bar)
      
    • super

      class FooBar(Foo, Bar):
          def __init__(self, bar='bar'):
              super().__init__()  # this calls all constructors up to Foo
              super(Foo, self).__init__(bar)  # this calls all constructors after Foo up
                                              # to Bar
      

    这两种方法各有其优点和缺点。如果你使用super,你的类将支持依赖注入。另一方面,容易出错。例如,如果你改变的顺序FooBar(像class FooBar(Bar, Foo)),你就必须更新super到匹配的电话。没有super您,不必担心这一点,并且代码更具可读性。

  2. 类之一是mixin。

    混入是,这是一个一流的设计与多重继承使用。这意味着我们不必手动调用两个父构造函数,因为mixin会自动为我们调用第二个构造函数。由于这次只需要调用一个构造函数,因此super可以避免对父类的名称进行硬编码。

    例:

    class FooMixin:
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)  # forwards all unused arguments
            self.foo = 'foo'
    
    class Bar:
        def __init__(self, bar):
            self.bar = bar
    
    class FooBar(FooMixin, Bar):
        def __init__(self, bar='bar'):
            super().__init__(bar)  # a single call is enough to invoke
                                   # all parent constructors
    
            # NOTE: `FooMixin.__init__(self, bar)` would also work, but isn't
            # recommended because we don't want to hard-code the parent class.
    

    这里的重要细节是:

    • mixin调用super().__init__()并通过它接收的任何参数。
    • 子类首先从mixin继承:class FooBar(FooMixin, Bar)。如果基类的顺序错误,则将永远不会调用mixin的构造函数。
  3. 所有基类均设计用于协作继承。

    专为合作继承而设计的类非常类似于mixin:它们将所有未使用的参数传递给下一类。和以前一样,我们只需要调用即可super().__init__(),所有父级构造函数都将被链调用。

    例:

    class CoopFoo:
        def __init__(self, **kwargs):
            super().__init__(**kwargs)  # forwards all unused arguments
            self.foo = 'foo'
    
    class CoopBar:
        def __init__(self, bar, **kwargs):
            super().__init__(**kwargs)  # forwards all unused arguments
            self.bar = bar
    
    class CoopFooBar(CoopFoo, CoopBar):
        def __init__(self, bar='bar'):
            super().__init__(bar=bar)  # pass all arguments on as keyword
                                       # arguments to avoid problems with
                                       # positional arguments and the order
                                       # of the parent classes
    

    在这种情况下,父类的顺序无关紧要。我们CoopBar最好还是从头继承,而代码仍然可以正常工作。但这是真的,因为所有参数都作为关键字参数传递。使用位置参数将很容易弄错参数的顺序,因此,协作类习惯于仅接受关键字参数。

    这也是我前面提到的规则的一个exceptions:CoopFooCoopBar都继承自object,但它们仍然调用super().__init__()。如果没有,则不会有合作继承。

底线:正确的实现取决于您从其继承的类。

构造函数是类的公共接口的一部分。如果该类被设计为混合或协作继承,则必须将其记录下来。如果文档中未提及任何内容,则可以安全地假定该类不是为协作多重继承设计的。

The answer to your question depends on one very important aspect: Are your base classes designed for multiple inheritance?

There are 3 different scenarios:

  1. The base classes are unrelated, standalone classes.

    If your base classes are separate entities that are capable of functioning independently and they don’t know each other, they’re not designed for multiple inheritance. Example:

    class Foo:
        def __init__(self):
            self.foo = 'foo'
    
    class Bar:
        def __init__(self, bar):
            self.bar = bar
    

    Important: Notice that neither Foo nor Bar calls super().__init__()! This is why your code didn’t work correctly. Because of the way diamond inheritance works in python, classes whose base class is object should not call super().__init__(). As you’ve noticed, doing so would break multiple inheritance because you end up calling another class’s __init__ rather than object.__init__(). (Disclaimer: Avoiding super().__init__() in object-subclasses is my personal recommendation and by no means an agreed-upon consensus in the python community. Some people prefer to use super in every class, arguing that you can always write an adapter if the class doesn’t behave as you expect.)

    This also means that you should never write a class that inherits from object and doesn’t have an __init__ method. Not defining a __init__ method at all has the same effect as calling super().__init__(). If your class inherits directly from object, make sure to add an empty constructor like so:

    class Base(object):
        def __init__(self):
            pass
    

    Anyway, in this situation, you will have to call each parent constructor manually. There are two ways to do this:

    • Without super

      class FooBar(Foo, Bar):
          def __init__(self, bar='bar'):
              Foo.__init__(self)  # explicit calls without super
              Bar.__init__(self, bar)
      
    • With super

      class FooBar(Foo, Bar):
          def __init__(self, bar='bar'):
              super().__init__()  # this calls all constructors up to Foo
              super(Foo, self).__init__(bar)  # this calls all constructors after Foo up
                                              # to Bar
      

    Each of these two methods has its own advantages and disadvantages. If you use super, your class will support dependency injection. On the other hand, it’s easier to make mistakes. For example if you change the order of Foo and Bar (like class FooBar(Bar, Foo)), you’d have to update the super calls to match. Without super you don’t have to worry about this, and the code is much more readable.

  2. One of the classes is a mixin.

    A mixin is a class that’s designed to be used with multiple inheritance. This means we don’t have to call both parent constructors manually, because the mixin will automatically call the 2nd constructor for us. Since we only have to call a single constructor this time, we can do so with super to avoid having to hard-code the parent class’s name.

    Example:

    class FooMixin:
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)  # forwards all unused arguments
            self.foo = 'foo'
    
    class Bar:
        def __init__(self, bar):
            self.bar = bar
    
    class FooBar(FooMixin, Bar):
        def __init__(self, bar='bar'):
            super().__init__(bar)  # a single call is enough to invoke
                                   # all parent constructors
    
            # NOTE: `FooMixin.__init__(self, bar)` would also work, but isn't
            # recommended because we don't want to hard-code the parent class.
    

    The important details here are:

    • The mixin calls super().__init__() and passes through any arguments it receives.
    • The subclass inherits from the mixin first: class FooBar(FooMixin, Bar). If the order of the base classes is wrong, the mixin’s constructor will never be called.
  3. All base classes are designed for cooperative inheritance.

    Classes designed for cooperative inheritance are a lot like mixins: They pass through all unused arguments to the next class. Like before, we just have to call super().__init__() and all parent constructors will be chain-called.

    Example:

    class CoopFoo:
        def __init__(self, **kwargs):
            super().__init__(**kwargs)  # forwards all unused arguments
            self.foo = 'foo'
    
    class CoopBar:
        def __init__(self, bar, **kwargs):
            super().__init__(**kwargs)  # forwards all unused arguments
            self.bar = bar
    
    class CoopFooBar(CoopFoo, CoopBar):
        def __init__(self, bar='bar'):
            super().__init__(bar=bar)  # pass all arguments on as keyword
                                       # arguments to avoid problems with
                                       # positional arguments and the order
                                       # of the parent classes
    

    In this case, the order of the parent classes doesn’t matter. We might as well inherit from CoopBar first, and the code would still work the same. But that’s only true because all arguments are passed as keyword arguments. Using positional arguments would make it easy to get the order of the arguments wrong, so it’s customary for cooperative classes to accept only keyword arguments.

    This is also an exception to the rule I mentioned earlier: Both CoopFoo and CoopBar inherit from object, but they still call super().__init__(). If they didn’t, there would be no cooperative inheritance.

Bottom line: The correct implementation depends on the classes you’re inheriting from.

The constructor is part of a class’s public interface. If the class is designed as a mixin or for cooperative inheritance, that must be documented. If the docs don’t mention anything of the sort, it’s safe to assume that the class isn’t designed for cooperative multiple inheritance.


回答 2

这两种方法(“新风格”或“旧式”),将工作,如果你有过的源代码控制AB。否则,可能需要使用适配器类。

可访问的源代码:正确使用“新样式”

class A(object):
    def __init__(self):
        print("-> A")
        super(A, self).__init__()
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        super(B, self).__init__()
        print("<- B")

class C(A, B):
    def __init__(self):
        print("-> C")
        # Use super here, instead of explicit calls to __init__
        super(C, self).__init__()
        print("<- C")
>>> C()
-> C
-> A
-> B
<- B
<- A
<- C

在此,方法解析顺序(MRO)规定以下内容:

  • C(A, B)A首先决定,然后B。MRO是C -> A -> B -> object
  • super(A, self).__init__()沿始于的MRO链继续C.__init__进行B.__init__
  • super(B, self).__init__()沿始于的MRO链继续C.__init__进行object.__init__

您可以说这种情况是为多重继承而设计的

可访问的源代码:正确使用“旧样式”

class A(object):
    def __init__(self):
        print("-> A")
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        # Don't use super here.
        print("<- B")

class C(A, B):
    def __init__(self):
        print("-> C")
        A.__init__(self)
        B.__init__(self)
        print("<- C")
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

在此,MRO无关紧要,因为A.__init__B.__init__被显式调用。class C(B, A):也会一样工作。

尽管这种情况不是像以前的样式那样“设计”为新样式的多重继承,但多重继承仍然是可能的。


现在,如果AB是从第三方库-即你有过的源代码没有控制AB?简短的答案:您必须设计一个实现必要super调用的适配器类,然后使用一个空类来定义MRO(请参阅Raymond Hettinger上的文章super -尤其是“如何合并非合作类”一节)。

第三方家长:A未实施superB确实

class A(object):
    def __init__(self):
        print("-> A")
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        super(B, self).__init__()
        print("<- B")

class Adapter(object):
    def __init__(self):
        print("-> C")
        A.__init__(self)
        super(Adapter, self).__init__()
        print("<- C")

class C(Adapter, B):
    pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

Adapter实现super是为了C定义MRO,该MRO在super(Adapter, self).__init__()执行时起作用。

如果反过来呢?

第三方父母:A工具superB才不是

class A(object):
    def __init__(self):
        print("-> A")
        super(A, self).__init__()
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        print("<- B")

class Adapter(object):
    def __init__(self):
        print("-> C")
        super(Adapter, self).__init__()
        B.__init__(self)
        print("<- C")

class C(Adapter, A):
    pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

此处的模式相同,除了执行顺序已切换Adapter.__init__super先呼叫,然后再进行显式呼叫。请注意,带有第三方父母的每种情况都需要一个唯一的适配器类。

因此,似乎除非我知道/控制我从(AB)继承的类的初始化,否则我无法对正在编写的类(C)做出安全选择。

虽然你可以处理,你没有的情况下,控制的源代码A,并B通过使用适配器类,这是事实,你必须知道在init怎样的父类实现super(如果有的话),以这样做。

Either approach (“new style” or “old style”) will work if you have control over the source code for A and B. Otherwise, use of an adapter class might be necessary.

Source code accessible: Correct use of “new style”

class A(object):
    def __init__(self):
        print("-> A")
        super(A, self).__init__()
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        super(B, self).__init__()
        print("<- B")

class C(A, B):
    def __init__(self):
        print("-> C")
        # Use super here, instead of explicit calls to __init__
        super(C, self).__init__()
        print("<- C")
>>> C()
-> C
-> A
-> B
<- B
<- A
<- C

Here, method resolution order (MRO) dictates the following:

  • C(A, B) dictates A first, then B. MRO is C -> A -> B -> object.
  • super(A, self).__init__() continues along the MRO chain initiated in C.__init__ to B.__init__.
  • super(B, self).__init__() continues along the MRO chain initiated in C.__init__ to object.__init__.

You could say that this case is designed for multiple inheritance.

Source code accessible: Correct use of “old style”

class A(object):
    def __init__(self):
        print("-> A")
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        # Don't use super here.
        print("<- B")

class C(A, B):
    def __init__(self):
        print("-> C")
        A.__init__(self)
        B.__init__(self)
        print("<- C")
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

Here, MRO does not matter, since A.__init__ and B.__init__ are called explicitly. class C(B, A): would work just as well.

Although this case is not “designed” for multiple inheritance in the new style as the previous one was, multiple inheritance is still possible.


Now, what if A and B are from a third party library – i.e., you have no control over the source code for A and B? The short answer: You must design an adapter class that implements the necessary super calls, then use an empty class to define the MRO (see Raymond Hettinger’s article on super – especially the section, “How to Incorporate a Non-cooperative Class”).

Third-party parents: A does not implement super; B does

class A(object):
    def __init__(self):
        print("-> A")
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        super(B, self).__init__()
        print("<- B")

class Adapter(object):
    def __init__(self):
        print("-> C")
        A.__init__(self)
        super(Adapter, self).__init__()
        print("<- C")

class C(Adapter, B):
    pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

Class Adapter implements super so that C can define the MRO, which comes into play when super(Adapter, self).__init__() is executed.

And what if it’s the other way around?

Third-party parents: A implements super; B does not

class A(object):
    def __init__(self):
        print("-> A")
        super(A, self).__init__()
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        print("<- B")

class Adapter(object):
    def __init__(self):
        print("-> C")
        super(Adapter, self).__init__()
        B.__init__(self)
        print("<- C")

class C(Adapter, A):
    pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

Same pattern here, except the order of execution is switched in Adapter.__init__; super call first, then explicit call. Notice that each case with third-party parents requires a unique adapter class.

So it seems that unless I know/control the init’s of the classes I inherit from (A and B) I cannot make a safe choice for the class I’m writing (C).

Although you can handle the cases where you don’t control the source code of A and B by using an adapter class, it is true that you must know how the init’s of the parent classes implement super (if at all) in order to do so.


回答 3

正如雷蒙德(Raymond)在回答中所说的那样,直接调用A.__init__B.__init__可以正常工作,并且您的代码易于阅读。

但是,它不使用C和这些类之间的继承链接。利用该链接可为您提供更多的一致性,并使最终的重构更加容易且不易出错。如何执行此操作的示例:

class C(A, B):
    def __init__(self):
        print("entering c")
        for base_class in C.__bases__:  # (A, B)
             base_class.__init__(self)
        print("leaving c")

As Raymond said in his answer, a direct call to A.__init__ and B.__init__ works fine, and your code would be readable.

However, it does not use the inheritance link between C and those classes. Exploiting that link gives you more consistancy and make eventual refactorings easier and less error-prone. An example of how to do that:

class C(A, B):
    def __init__(self):
        print("entering c")
        for base_class in C.__bases__:  # (A, B)
             base_class.__init__(self)
        print("leaving c")

回答 4

本文有助于解释协作式多重继承:

http://www.artima.com/weblogs/viewpost.jsp?thread=281127

它提到了有用的方法mro(),可向您显示方法解析顺序。在你的第二个例子,当你调用superA,该super呼叫继续在MRO。顺序中的下一个类是B,这就是为什么Binit首次被调用的原因。

这是来自python官方站点的更多技术文章:

http://www.python.org/download/releases/2.3/mro/

This article helps to explain cooperative multiple inheritance:

http://www.artima.com/weblogs/viewpost.jsp?thread=281127

It mentions the useful method mro() that shows you the method resolution order. In your 2nd example, where you call super in A, the super call continues on in MRO. The next class in the order is B, this is why B‘s init is called the first time.

Here’s a more technical article from the official python site:

http://www.python.org/download/releases/2.3/mro/


回答 5

如果要从第三方库中繁衍子类类,则不会,没有盲目的方法来调用__init__实际上起作用的基类方法(或任何其他方法),而不管基类的编程方式如何。

super使编写旨在协作实现方法的类成为复杂的多重继承树的一部分成为可能,而类继承者不必知道。但是无法使用它正确地从可能使用或可能不使用的任意类中继承super

本质上,一个类是设计为使用super基类还是直接调用基类来进行子类化,是属于该类“公共接口”一部分的属性,因此应进行记录。如果您以库作者所期望的方式使用第三方库,并且库具有合理的文档,则通常会告诉您需要做什么来对特定的事物进行子类化。如果不是,那么您必须查看要子类化的类的源代码,并查看其基类调用约定是什么。如果你是从一个或多个第三方库的方式,该库作者结合多个类想到,那么它可能无法始终如一地调用超类的方法在所有; 如果类A是使用的层次结构的一部分,super而类B是不使用super的层次结构的一部分,则不能保证这两种选择都不会起作用。您将必须找出一种适用于每个特定案例的策略。

If you are multiply sub-classing classes from third party libraries, then no, there is no blind approach to calling the base class __init__ methods (or any other methods) that actually works regardless of how the base classes are programmed.

super makes it possible to write classes designed to cooperatively implement methods as part of complex multiple inheritance trees which need not be known to the class author. But there’s no way to use it to correctly inherit from arbitrary classes that may or may not use super.

Essentially, whether a class is designed to be sub-classed using super or with direct calls to the base class is a property which is part of the class’ “public interface”, and it should be documented as such. If you’re using third-party libraries in the way that the library author expected and the library has reasonable documentation, it would normally tell you what you are required to do to subclass particular things. If not, then you’ll have to look at the source code for the classes you’re sub-classing and see what their base-class-invocation convention is. If you’re combining multiple classes from one or more third-party libraries in a way that the library authors didn’t expect, then it may not be possible to consistently invoke super-class methods at all; if class A is part of a hierarchy using super and class B is part of a hierarchy that doesn’t use super, then neither option is guaranteed to work. You’ll have to figure out a strategy that happens to work for each particular case.


什么时候应该在Python中使用类?

问题:什么时候应该在Python中使用类?

我已经用python编程了大约两年了。主要是数据(pandas,mpl,numpy),还有自动化脚本和小型Web应用程序。我试图成为一个更好的程序员,并增加我的python知识,而困扰我的一件事是我从未使用过一个类(除了为小型Web应用程序复制随机烧瓶代码外)。我通常理解它们是什么,但是我似乎无法为为什么在一个简单的函数中需要它们的问题而wrap之以鼻。

为了使我的问题更具针对性:我编写了大量的自动报告,这些报告总是涉及从多个数据源(mongo,sql,postgres,api)中提取数据,执行一些或少量的数据整理和格式化,将数据写入csv / excel / html,通过电子邮件发送出去。脚本范围从〜250行到〜600行。我有什么理由要使用类来做到这一点吗?为什么?

I have been programming in python for about two years; mostly data stuff (pandas, mpl, numpy), but also automation scripts and small web apps. I’m trying to become a better programmer and increase my python knowledge and one of the things that bothers me is that I have never used a class (outside of copying random flask code for small web apps). I generally understand what they are, but I can’t seem to wrap my head around why I would need them over a simple function.

To add specificity to my question: I write tons of automated reports which always involve pulling data from multiple data sources (mongo, sql, postgres, apis), performing a lot or a little data munging and formatting, writing the data to csv/excel/html, send it out in an email. The scripts range from ~250 lines to ~600 lines. Would there be any reason for me to use classes to do this and why?


回答 0

类是面向对象程序设计的支柱。OOP高度关注代码的组织,可重用性和封装。

首先,免责声明:OOP与函数式编程在某种程度上相反,后者是Python中经常使用的一种不同范例。并非每个使用Python(或肯定是大多数语言)编程的人都使用OOP。在Java 8中,您可以做很多事情,而这些都不是面向对象的。如果您不想使用OOP,请不要使用。如果您只是编写一次性脚本来处理将不再使用的数据,那么请按原样编写。

但是,使用OOP的原因很多。

原因如下:

  • 组织:OOP定义了在代码中描述和定义数据与过程的众所周知的标准方法。数据和过程都可以存储在不同的定义级别(在不同的类中),并且有谈论这些定义的标准方法。也就是说,如果您以标准方式使用OOP,它将帮助您以后的自己和他人理解,编辑和使用您的代码。同样,您可以使用数据结构的名称并方便地引用它们,而不是使用复杂的任意数据存储机制(命令或列表的命令,集合的命令或列表的命令或其他命令)。

  • 状态:OOP可帮助您定义和跟踪状态。例如,在一个经典的示例中,如果您要创建一个处理学生的程序(例如,年级程序),则可以将您需要的所有有关他们的信息都保留在一个位置(姓名,年龄,性别,年级,类,年级,教师,同龄人,饮食,特殊需求等),并且只要对象还活着并且可以轻松访问,此数据就会保留下来。

  • 封装:通过封装,过程和数据一起存储。方法(功能的OOP术语)与操作和产生的数据一起定义。在像Java这样的允许访问控制的语言中,或者在Python中,取决于您描述公共API的方式,这意味着可以向用户隐藏方法和数据。这意味着,如果您需要或想要更改代码,则可以对代码的实现做任何您想做的事,但要使公共API保持不变。

  • 继承:通过继承,您可以在一个位置(一个类)中定义数据和过程,然后在以后覆盖或扩展该功能。例如,在Python中,我经常看到人们创建dict该类的子类以添加其他功能。常见的更改是覆盖从不存在的字典中请求键以基于未知键提供默认值时引发异常的方法。这允许您现在或以后扩展自己的代码,允许其他人扩展您的代码,并允许您扩展其他人的代码。

  • 可重用性:所有这些原因以及其他原因都可以提高代码的可重用性。面向对象的代码使您可以编写可靠的(经过测试的)代码一次,然后反复使用。如果需要针对特定​​用例进行调整,则可以从现有的类继承并覆盖现有的行为。如果您需要更改某些内容,则可以在保留现有公共方法签名的同时进行全部更改,并且没有一个人是明智的(希望如此)。

同样,有几个原因不使用OOP,而您则不需要。但是幸运的是,使用Python之类的语言,您可以使用一点或很多,这取决于您。

学生用例的一个示例(不能保证代码质量,仅是一个示例):

面向对象

class Student(object):
    def __init__(self, name, age, gender, level, grades=None):
        self.name = name
        self.age = age
        self.gender = gender
        self.level = level
        self.grades = grades or {}

    def setGrade(self, course, grade):
        self.grades[course] = grade

    def getGrade(self, course):
        return self.grades[course]

    def getGPA(self):
        return sum(self.grades.values())/len(self.grades)

# Define some students
john = Student("John", 12, "male", 6, {"math":3.3})
jane = Student("Jane", 12, "female", 6, {"math":3.5})

# Now we can get to the grades easily
print(john.getGPA())
print(jane.getGPA())

标准区

def calculateGPA(gradeDict):
    return sum(gradeDict.values())/len(gradeDict)

students = {}
# We can set the keys to variables so we might minimize typos
name, age, gender, level, grades = "name", "age", "gender", "level", "grades"
john, jane = "john", "jane"
math = "math"
students[john] = {}
students[john][age] = 12
students[john][gender] = "male"
students[john][level] = 6
students[john][grades] = {math:3.3}

students[jane] = {}
students[jane][age] = 12
students[jane][gender] = "female"
students[jane][level] = 6
students[jane][grades] = {math:3.5}

# At this point, we need to remember who the students are and where the grades are stored. Not a huge deal, but avoided by OOP.
print(calculateGPA(students[john][grades]))
print(calculateGPA(students[jane][grades]))

Classes are the pillar of Object Oriented Programming. OOP is highly concerned with code organization, reusability, and encapsulation.

First, a disclaimer: OOP is partially in contrast to Functional Programming, which is a different paradigm used a lot in Python. Not everyone who programs in Python (or surely most languages) uses OOP. You can do a lot in Java 8 that isn’t very Object Oriented. If you don’t want to use OOP, then don’t. If you’re just writing one-off scripts to process data that you’ll never use again, then keep writing the way you are.

However, there are a lot of reasons to use OOP.

Some reasons:

  • Organization: OOP defines well known and standard ways of describing and defining both data and procedure in code. Both data and procedure can be stored at varying levels of definition (in different classes), and there are standard ways about talking about these definitions. That is, if you use OOP in a standard way, it will help your later self and others understand, edit, and use your code. Also, instead of using a complex, arbitrary data storage mechanism (dicts of dicts or lists or dicts or lists of dicts of sets, or whatever), you can name pieces of data structures and conveniently refer to them.

  • State: OOP helps you define and keep track of state. For instance, in a classic example, if you’re creating a program that processes students (for instance, a grade program), you can keep all the info you need about them in one spot (name, age, gender, grade level, courses, grades, teachers, peers, diet, special needs, etc.), and this data is persisted as long as the object is alive, and is easily accessible.

  • Encapsulation: With encapsulation, procedure and data are stored together. Methods (an OOP term for functions) are defined right alongside the data that they operate on and produce. In a language like Java that allows for access control, or in Python, depending upon how you describe your public API, this means that methods and data can be hidden from the user. What this means is that if you need or want to change code, you can do whatever you want to the implementation of the code, but keep the public APIs the same.

  • Inheritance: Inheritance allows you to define data and procedure in one place (in one class), and then override or extend that functionality later. For instance, in Python, I often see people creating subclasses of the dict class in order to add additional functionality. A common change is overriding the method that throws an exception when a key is requested from a dictionary that doesn’t exist to give a default value based on an unknown key. This allows you to extend your own code now or later, allow others to extend your code, and allows you to extend other people’s code.

  • Reusability: All of these reasons and others allow for greater reusability of code. Object oriented code allows you to write solid (tested) code once, and then reuse over and over. If you need to tweak something for your specific use case, you can inherit from an existing class and overwrite the existing behavior. If you need to change something, you can change it all while maintaining the existing public method signatures, and no one is the wiser (hopefully).

Again, there are several reasons not to use OOP, and you don’t need to. But luckily with a language like Python, you can use just a little bit or a lot, it’s up to you.

An example of the student use case (no guarantee on code quality, just an example):

Object Oriented

class Student(object):
    def __init__(self, name, age, gender, level, grades=None):
        self.name = name
        self.age = age
        self.gender = gender
        self.level = level
        self.grades = grades or {}

    def setGrade(self, course, grade):
        self.grades[course] = grade

    def getGrade(self, course):
        return self.grades[course]

    def getGPA(self):
        return sum(self.grades.values())/len(self.grades)

# Define some students
john = Student("John", 12, "male", 6, {"math":3.3})
jane = Student("Jane", 12, "female", 6, {"math":3.5})

# Now we can get to the grades easily
print(john.getGPA())
print(jane.getGPA())

Standard Dict

def calculateGPA(gradeDict):
    return sum(gradeDict.values())/len(gradeDict)

students = {}
# We can set the keys to variables so we might minimize typos
name, age, gender, level, grades = "name", "age", "gender", "level", "grades"
john, jane = "john", "jane"
math = "math"
students[john] = {}
students[john][age] = 12
students[john][gender] = "male"
students[john][level] = 6
students[john][grades] = {math:3.3}

students[jane] = {}
students[jane][age] = 12
students[jane][gender] = "female"
students[jane][level] = 6
students[jane][grades] = {math:3.5}

# At this point, we need to remember who the students are and where the grades are stored. Not a huge deal, but avoided by OOP.
print(calculateGPA(students[john][grades]))
print(calculateGPA(students[jane][grades]))

回答 1

每当您需要维护函数状态时,都无法使用生成器来实现(生成而不是返回的函数)。生成器保持自己的状态。

如果要覆盖任何标准运算符,则需要一个类。

每当您用于访问者模式时,就需要使用类。使用生成器,上下文管理器(与生成器相比,比作为类更好地实现)和POD类型(字典,列表和元组等),可以更有效,更干净地完成所有其他设计模式。

如果要编写“ pythonic”代码,则应优先使用上下文管理器和生成器,而不要使用类。会更干净。

如果要扩展功能,几乎总是可以通过包含而不是继承来实现它。

每个规则都有exceptions。如果要快速封装功能(即编写测试代码而不是库级别的可重用代码),则可以将状态封装在类中。这很简单,不需要重用。

如果您需要C ++样式析构函数(RIIA),则绝对不希望使用类。您需要上下文管理器。

Whenever you need to maintain a state of your functions and it cannot be accomplished with generators (functions which yield rather than return). Generators maintain their own state.

If you want to override any of the standard operators, you need a class.

Whenever you have a use for a Visitor pattern, you’ll need classes. Every other design pattern can be accomplished more effectively and cleanly with generators, context managers (which are also better implemented as generators than as classes) and POD types (dictionaries, lists and tuples, etc.).

If you want to write “pythonic” code, you should prefer context managers and generators over classes. It will be cleaner.

If you want to extend functionality, you will almost always be able to accomplish it with containment rather than inheritance.

As every rule, this has an exception. If you want to encapsulate functionality quickly (ie, write test code rather than library-level reusable code), you can encapsulate the state in a class. It will be simple and won’t need to be reusable.

If you need a C++ style destructor (RIIA), you definitely do NOT want to use classes. You want context managers.


回答 2

我想你做对了。当您需要模拟一些业务逻辑或具有困难关系的困难现实过程时,类是合理的。例如:

  • 具有共享状态的几个功能
  • 多个相同状态变量的副本
  • 扩展现有功能的行为

我也建议您观看这部经典影片

I think you do it right. Classes are reasonable when you need to simulate some business logic or difficult real-life processes with difficult relations. As example:

  • Several functions with share state
  • More than one copy of the same state variables
  • To extend the behavior of an existing functionality

I also suggest you to watch this classic video


回答 3

一个类定义了一个现实世界的实体。如果您正在处理独立存在的事物,并且具有与其他事物不同的自己的逻辑,则应该为其创建一个类。例如,一个封装数据库连接的类。

如果不是这种情况,则无需创建类

A class defines a real world entity. If you are working on something that exists individually and has its own logic that is separate from others, you should create a class for it. For example, a class that encapsulates database connectivity.

If this not the case, no need to create class


回答 4

这取决于您的想法和设计。如果您是一位优秀的设计师,那么OOP会以各种设计模式的形式自然出现。对于简单的脚本级别,处理OOP可能会产生开销。简单考虑一下OOP的基本好处,例如可重用和可扩展,并确定是否需要它们。OOP使复杂的事情变得越来越简单。使用OOP或不使用OOP都可以使事情简单。使用哪个更简单。

Its depends on your idea and design. if you are good designer than OOPs will come out naturally in the form of various design patterns. For a simple script level processing OOPs can be overhead. Simple consider the basic benefits of OOPs like reusable and extendable and make sure if they are needed or not. OOPs make complex things simpler and simpler things complex. Simply keeps the things simple in either way using OOPs or not Using OOPs. which ever is simpler use that.


OpenCV –未校准立体声系统的深度图

问题:OpenCV –未校准立体声系统的深度图

我正在尝试使用未经校准的方法获得深度图。我可以通过使用SIFT查找对应点然后使用来获得基本矩阵cv2.findFundamentalMat。然后cv2.stereoRectifyUncalibrated,我用于获取每个图像的单应性矩阵。最后,我使用它cv2.warpPerspective来校正和计算视差,但这并不能创建良好的深度图。值非常高,所以我想知道是否必须使用warpPerspective或是否必须根据所获得的单应性矩阵计算旋转矩阵stereoRectifyUncalibrated

我不确定投影矩阵是否与通过校正得到的单应矩阵有关stereoRectifyUncalibrated

代码的一部分:

#Obtainment of the correspondent point with SIFT
sift = cv2.SIFT()

###find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(dst1,None)
kp2, des2 = sift.detectAndCompute(dst2,None)

###FLANN parameters
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)

flann = cv2.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)

good = []
pts1 = []
pts2 = []

###ratio test as per Lowe's paper
for i,(m,n) in enumerate(matches):
    if m.distance < 0.8*n.distance:
        good.append(m)
        pts2.append(kp2[m.trainIdx].pt)
        pts1.append(kp1[m.queryIdx].pt)
    
    
pts1 = np.array(pts1)
pts2 = np.array(pts2)

#Computation of the fundamental matrix
F,mask= cv2.findFundamentalMat(pts1,pts2,cv2.FM_LMEDS)


# Obtainment of the rectification matrix and use of the warpPerspective to transform them...
pts1 = pts1[:,:][mask.ravel()==1]
pts2 = pts2[:,:][mask.ravel()==1]

pts1 = np.int32(pts1)
pts2 = np.int32(pts2)

p1fNew = pts1.reshape((pts1.shape[0] * 2, 1))
p2fNew = pts2.reshape((pts2.shape[0] * 2, 1))
    
retBool ,rectmat1, rectmat2 = cv2.stereoRectifyUncalibrated(p1fNew,p2fNew,F,(2048,2048))

dst11 = cv2.warpPerspective(dst1,rectmat1,(2048,2048))
dst22 = cv2.warpPerspective(dst2,rectmat2,(2048,2048))

#calculation of the disparity
stereo = cv2.StereoBM(cv2.STEREO_BM_BASIC_PRESET,ndisparities=16*10, SADWindowSize=9)
disp = stereo.compute(dst22.astype(uint8), dst11.astype(uint8)).astype(np.float32)
plt.imshow(disp);plt.colorbar();plt.clim(0,400)#;plt.show()
plt.savefig("0gauche.png")

#plot depth by using disparity focal length `C1[0,0]` from stereo calibration and `T[0]` the distance between cameras

plt.imshow(C1[0,0]*T[0]/(disp),cmap='hot');plt.clim(-0,500);plt.colorbar();plt.show()

这是使用未校准方法(和warpPerspective)校正后的照片:

这是使用校准方法校正后的图片:

我不知道两种图片之间的区别有多么重要。对于校准方法,它似乎没有对齐。

使用未校准方法的视差图:

:深度计算与C1[0,0]*T[0]/(disp) 从以T stereoCalibrate。该值很高。

————编辑后————

我试图用通过“ stereoRectifyUncalibrated”获得的单应性矩阵“装载”重构矩阵([Devernay97][Garcia01]),但结果仍然不理想。我这样做正确吗?

Y=np.arange(0,2048)
X=np.arange(0,2048)
(XX_field,YY_field)=np.meshgrid(X,Y)

#I mount the X, Y and disparity in a same 3D array 
stock = np.concatenate((np.expand_dims(XX_field,2),np.expand_dims(YY_field,2)),axis=2)
XY_disp = np.concatenate((stock,np.expand_dims(disp,2)),axis=2)

XY_disp_reshape = XY_disp.reshape(XY_disp.shape[0]*XY_disp.shape[1],3)

Ts = np.hstack((np.zeros((3,3)),T_0)) #i use only the translations obtained with the rectified calibration...Is it correct?


# I establish the projective matrix with the homography matrix
P11 = np.dot(rectmat1,C1)
P1 = np.vstack((np.hstack((P11,np.zeros((3,1)))),np.zeros((1,4))))
P1[3,3] = 1

# P1 = np.dot(C1,np.hstack((np.identity(3),np.zeros((3,1)))))

P22 = np.dot(np.dot(rectmat2,C2),Ts)
P2 = np.vstack((P22,np.zeros((1,4))))
P2[3,3] = 1

lambda_t = cv2.norm(P1[0,:].T)/cv2.norm(P2[0,:].T)


#I define the reconstruction matrix
Q = np.zeros((4,4))

Q[0,:] = P1[0,:].T
Q[1,:] = P1[1,:].T
Q[2,:] = lambda_t*P2[1,:].T - P1[1,:].T
Q[3,:] = P1[2,:].T

#I do the calculation to get my 3D coordinates
test = []
for i in range(0,XY_disp_reshape.shape[0]):
    a = np.dot(inv(Q),np.expand_dims(np.concatenate((XY_disp_reshape[i,:],np.ones((1))),axis=0),axis=1))
    test.append(a)

test = np.asarray(test)

XYZ = test[:,:,0].reshape(XY_disp.shape[0],XY_disp.shape[1],4)

I’m trying to get a depth map with an uncalibrated method. I can obtain the fundamental matrix by finding correspondent points with SIFT and then using cv2.findFundamentalMat. I then use cv2.stereoRectifyUncalibrated to get the homography matrices for each image. Finally I use cv2.warpPerspective to rectify and compute the disparity, but this doesn’t create a good depth map. The values are very high so I’m wondering if I have to use warpPerspective or if I have to calculate a rotation matrix from the homography matrices I got with stereoRectifyUncalibrated.

I’m not sure of the projective matrix with the case of homography matrix obtained with the stereoRectifyUncalibrated to rectify.

A part of the code:

#Obtainment of the correspondent point with SIFT
sift = cv2.SIFT()

###find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(dst1,None)
kp2, des2 = sift.detectAndCompute(dst2,None)

###FLANN parameters
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)

flann = cv2.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)

good = []
pts1 = []
pts2 = []

###ratio test as per Lowe's paper
for i,(m,n) in enumerate(matches):
    if m.distance < 0.8*n.distance:
        good.append(m)
        pts2.append(kp2[m.trainIdx].pt)
        pts1.append(kp1[m.queryIdx].pt)
    
    
pts1 = np.array(pts1)
pts2 = np.array(pts2)

#Computation of the fundamental matrix
F,mask= cv2.findFundamentalMat(pts1,pts2,cv2.FM_LMEDS)


# Obtainment of the rectification matrix and use of the warpPerspective to transform them...
pts1 = pts1[:,:][mask.ravel()==1]
pts2 = pts2[:,:][mask.ravel()==1]

pts1 = np.int32(pts1)
pts2 = np.int32(pts2)

p1fNew = pts1.reshape((pts1.shape[0] * 2, 1))
p2fNew = pts2.reshape((pts2.shape[0] * 2, 1))
    
retBool ,rectmat1, rectmat2 = cv2.stereoRectifyUncalibrated(p1fNew,p2fNew,F,(2048,2048))

dst11 = cv2.warpPerspective(dst1,rectmat1,(2048,2048))
dst22 = cv2.warpPerspective(dst2,rectmat2,(2048,2048))

#calculation of the disparity
stereo = cv2.StereoBM(cv2.STEREO_BM_BASIC_PRESET,ndisparities=16*10, SADWindowSize=9)
disp = stereo.compute(dst22.astype(uint8), dst11.astype(uint8)).astype(np.float32)
plt.imshow(disp);plt.colorbar();plt.clim(0,400)#;plt.show()
plt.savefig("0gauche.png")

#plot depth by using disparity focal length `C1[0,0]` from stereo calibration and `T[0]` the distance between cameras

plt.imshow(C1[0,0]*T[0]/(disp),cmap='hot');plt.clim(-0,500);plt.colorbar();plt.show()

Here are the rectified pictures with the uncalibrated method (and warpPerspective):

Here are the rectified pictures with the calibrated method:

I don’t know how the difference is so important between the two kind of pictures. And for the calibrated method, it doesn’t seem aligned.

The disparity map using the uncalibrated method:

The depths are calculated with : C1[0,0]*T[0]/(disp) with T from the stereoCalibrate. The values are very high.

———— EDIT LATER ————

I tried to “mount” the reconstruction matrix ([Devernay97], [Garcia01]) with the homography matrix obtained with “stereoRectifyUncalibrated”, but the result is still not good. Am I doing this correctly?

Y=np.arange(0,2048)
X=np.arange(0,2048)
(XX_field,YY_field)=np.meshgrid(X,Y)

#I mount the X, Y and disparity in a same 3D array 
stock = np.concatenate((np.expand_dims(XX_field,2),np.expand_dims(YY_field,2)),axis=2)
XY_disp = np.concatenate((stock,np.expand_dims(disp,2)),axis=2)

XY_disp_reshape = XY_disp.reshape(XY_disp.shape[0]*XY_disp.shape[1],3)

Ts = np.hstack((np.zeros((3,3)),T_0)) #i use only the translations obtained with the rectified calibration...Is it correct?


# I establish the projective matrix with the homography matrix
P11 = np.dot(rectmat1,C1)
P1 = np.vstack((np.hstack((P11,np.zeros((3,1)))),np.zeros((1,4))))
P1[3,3] = 1

# P1 = np.dot(C1,np.hstack((np.identity(3),np.zeros((3,1)))))

P22 = np.dot(np.dot(rectmat2,C2),Ts)
P2 = np.vstack((P22,np.zeros((1,4))))
P2[3,3] = 1

lambda_t = cv2.norm(P1[0,:].T)/cv2.norm(P2[0,:].T)


#I define the reconstruction matrix
Q = np.zeros((4,4))

Q[0,:] = P1[0,:].T
Q[1,:] = P1[1,:].T
Q[2,:] = lambda_t*P2[1,:].T - P1[1,:].T
Q[3,:] = P1[2,:].T

#I do the calculation to get my 3D coordinates
test = []
for i in range(0,XY_disp_reshape.shape[0]):
    a = np.dot(inv(Q),np.expand_dims(np.concatenate((XY_disp_reshape[i,:],np.ones((1))),axis=0),axis=1))
    test.append(a)

test = np.asarray(test)

XYZ = test[:,:,0].reshape(XY_disp.shape[0],XY_disp.shape[1],4)

回答 0

TLDR;对边缘更平滑的图像使用StereoSGBM(半全局块匹配),如果您希望它仍然更平滑,请使用一些后期过滤

OP没有提供原始图像,因此我使用Tsukuba的是Middlebury数据集

常规StereoBM的结果

StereoSGBM的结果(已调整)

我在文学中能找到的最好结果

有关详细信息,请参见此处的出版物。

后过滤示例(请参见下面的链接)

OP的问题的理论/其他考虑

校正后的校正图像中较大的黑色区域使我相信,对于这些校正效果不是很好。可能有多种原因在起作用,例如物理设置,校准时可能亮起的照明等等,但是为此有很多相机校准教程,我的理解是,您正在寻找一种方法来从未经校准的设置中获得更好的深度图(虽然不是100%清晰,但是标题似乎支持这一点,我认为这就是人们来这里寻找的地方)。

您的基本方法是正确的,但结果肯定可以改善。深度映射的这种形式不在产生最高质量的映射的那些之中(尤其是未经校准的)。最大的改进可能来自使用不同的立体声匹配算法。照明也可能会产生重大影响。正确的图像(至少对我的肉眼而言)似乎光线不足,可能会干扰重建。您可以先尝试将其亮度提高到另一个水平,或者在可能的情况下收集新图像。从这里开始,我将假定您无权使用原始相机,因此,我将考虑收集新图像,更改设置或执行校准超出范围。(如果您确实有权访问设置和摄像机,

您过去曾StereoBM计算过有效的视差(深度图),但StereoSGBM更适合此应用程序(更好地处理更平滑的边缘)。您可以在下面看到区别。

本文更深入地解释了这些差异:

块匹配专注于高纹理图像(想像一棵树的图片),而半全局块匹配则专注于子像素级匹配和纹理更平滑的图片(想像走廊的图片)。

如果没有任何明确的固有摄像机参数,有关摄像机设置的详细信息(例如焦距,摄像机之间的距离,到被摄物体的距离等),图像中的已知尺寸或运动(以使用来自运动的结构),则可以仅获得3D重建,直到投影变换;您也不会有比例感或旋转感,但是仍然可以生成相对深度图。您可能会遭受一些镜筒变形和其他变形的困扰,这些变形可以通过适当的相机校准来消除,但是只要相机不可怕(镜头系统不太失真)并且设置得相当漂亮,您就可以获得合理的结果。接近规范配置(这基本上意味着它们的方向应使其光轴尽可能接近平行,并且它们的视场充分重叠)。但是,这似乎不是OP的问题,因为他确实设法通过未校准的方法获得了正确的校正图像。

基本程序

  1. 在两张图像中至少找到5个匹配良好的点,可以用来计算基本矩阵(可以使用任何喜欢的检测器和匹配器,我保留了FLANN,但是使用ORB进行了检测,因为SIFT不在OpenCV的主版本中对于4.2.0)
  2. 用以下公式计算基本矩阵F findFundamentalMat
  3. 使用stereoRectifyUncalibrated和取消图像失真warpPerspective
  4. 计算视差(深度图) StereoSGBM

结果要好得多:

与ORB和FLANN匹配

未失真的图像(先左后右)


差距

立体声BM

此结果看起来与OP的问题(斑点,间隙,某些区域的错误深度)相似。

StereoSGBM(已调整)

这个结果看起来要好得多,并且使用与OP大致相同的方法,减去最终的视差计算,这让我认为,如果提供OP,OP将在他的图像上看到类似的改进。

后过滤

OpenCV文档中有一篇很好的文章。如果您需要非常平滑的地图,建议您查看一下。

上面的示例照片是MPI Sintel数据ambush_2集中场景的第1帧。

完整代码(在OpenCV 4.2.0上测试):

import cv2
import numpy as np
import matplotlib.pyplot as plt

imgL = cv2.imread("tsukuba_l.png", cv2.IMREAD_GRAYSCALE)  # left image
imgR = cv2.imread("tsukuba_r.png", cv2.IMREAD_GRAYSCALE)  # right image


def get_keypoints_and_descriptors(imgL, imgR):
    """Use ORB detector and FLANN matcher to get keypoints, descritpors,
    and corresponding matches that will be good for computing
    homography.
    """
    orb = cv2.ORB_create()
    kp1, des1 = orb.detectAndCompute(imgL, None)
    kp2, des2 = orb.detectAndCompute(imgR, None)

    ############## Using FLANN matcher ##############
    # Each keypoint of the first image is matched with a number of
    # keypoints from the second image. k=2 means keep the 2 best matches
    # for each keypoint (best matches = the ones with the smallest
    # distance measurement).
    FLANN_INDEX_LSH = 6
    index_params = dict(
        algorithm=FLANN_INDEX_LSH,
        table_number=6,  # 12
        key_size=12,  # 20
        multi_probe_level=1,
    )  # 2
    search_params = dict(checks=50)  # or pass empty dictionary
    flann = cv2.FlannBasedMatcher(index_params, search_params)
    flann_match_pairs = flann.knnMatch(des1, des2, k=2)
    return kp1, des1, kp2, des2, flann_match_pairs


def lowes_ratio_test(matches, ratio_threshold=0.6):
    """Filter matches using the Lowe's ratio test.

    The ratio test checks if matches are ambiguous and should be
    removed by checking that the two distances are sufficiently
    different. If they are not, then the match at that keypoint is
    ignored.

    /programming/51197091/how-does-the-lowes-ratio-test-work
    """
    filtered_matches = []
    for m, n in matches:
        if m.distance < ratio_threshold * n.distance:
            filtered_matches.append(m)
    return filtered_matches


def draw_matches(imgL, imgR, kp1, des1, kp2, des2, flann_match_pairs):
    """Draw the first 8 mathces between the left and right images."""
    # https://docs.opencv.org/4.2.0/d4/d5d/group__features2d__draw.html
    # https://docs.opencv.org/2.4/modules/features2d/doc/common_interfaces_of_descriptor_matchers.html
    img = cv2.drawMatches(
        imgL,
        kp1,
        imgR,
        kp2,
        flann_match_pairs[:8],
        None,
        flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS,
    )
    cv2.imshow("Matches", img)
    cv2.imwrite("ORB_FLANN_Matches.png", img)
    cv2.waitKey(0)


def compute_fundamental_matrix(matches, kp1, kp2, method=cv2.FM_RANSAC):
    """Use the set of good mathces to estimate the Fundamental Matrix.

    See  https://en.wikipedia.org/wiki/Eight-point_algorithm#The_normalized_eight-point_algorithm
    for more info.
    """
    pts1, pts2 = [], []
    fundamental_matrix, inliers = None, None
    for m in matches[:8]:
        pts1.append(kp1[m.queryIdx].pt)
        pts2.append(kp2[m.trainIdx].pt)
    if pts1 and pts2:
        # You can play with the Threshold and confidence values here
        # until you get something that gives you reasonable results. I
        # used the defaults
        fundamental_matrix, inliers = cv2.findFundamentalMat(
            np.float32(pts1),
            np.float32(pts2),
            method=method,
            # ransacReprojThreshold=3,
            # confidence=0.99,
        )
    return fundamental_matrix, inliers, pts1, pts2


############## Find good keypoints to use ##############
kp1, des1, kp2, des2, flann_match_pairs = get_keypoints_and_descriptors(imgL, imgR)
good_matches = lowes_ratio_test(flann_match_pairs, 0.2)
draw_matches(imgL, imgR, kp1, des1, kp2, des2, good_matches)


############## Compute Fundamental Matrix ##############
F, I, points1, points2 = compute_fundamental_matrix(good_matches, kp1, kp2)


############## Stereo rectify uncalibrated ##############
h1, w1 = imgL.shape
h2, w2 = imgR.shape
thresh = 0
_, H1, H2 = cv2.stereoRectifyUncalibrated(
    np.float32(points1), np.float32(points2), F, imgSize=(w1, h1), threshold=thresh,
)

############## Undistort (Rectify) ##############
imgL_undistorted = cv2.warpPerspective(imgL, H1, (w1, h1))
imgR_undistorted = cv2.warpPerspective(imgR, H2, (w2, h2))
cv2.imwrite("undistorted_L.png", imgL_undistorted)
cv2.imwrite("undistorted_R.png", imgR_undistorted)

############## Calculate Disparity (Depth Map) ##############

# Using StereoBM
stereo = cv2.StereoBM_create(numDisparities=16, blockSize=15)
disparity_BM = stereo.compute(imgL_undistorted, imgR_undistorted)
plt.imshow(disparity_BM, "gray")
plt.colorbar()
plt.show()

# Using StereoSGBM
# Set disparity parameters. Note: disparity range is tuned according to
#  specific parameters obtained through trial and error.
win_size = 2
min_disp = -4
max_disp = 9
num_disp = max_disp - min_disp  # Needs to be divisible by 16
stereo = cv2.StereoSGBM_create(
    minDisparity=min_disp,
    numDisparities=num_disp,
    blockSize=5,
    uniquenessRatio=5,
    speckleWindowSize=5,
    speckleRange=5,
    disp12MaxDiff=2,
    P1=8 * 3 * win_size ** 2,
    P2=32 * 3 * win_size ** 2,
)
disparity_SGBM = stereo.compute(imgL_undistorted, imgR_undistorted)
plt.imshow(disparity_SGBM, "gray")
plt.colorbar()
plt.show()

TLDR; Use StereoSGBM (Semi Global Block Matching) for images with smoother edges and use some post filtering if you want it smoother still

OP didn’t provide original images, so I’m using Tsukuba from the Middlebury data set.

Result with regular StereoBM

Result with StereoSGBM (tuned)

Best result I could find in literature

See the publication here for details.

Example of post filtering (see link below)

Theory/Other considerations from OP’s question

The large black areas of your calibrated rectified images would lead me to believe that for those, calibration was not done very well. There’s a variety of reasons that could be at play, maybe the physical setup, maybe lighting when you did calibration, etc., but there are plenty of camera calibration tutorials out there for that and my understanding is that you are asking for a way to get a better depth map from an uncalibrated setup (this isn’t 100% clear, but the title seems to support this and I think that’s what people will come here to try to find).

Your basic approach is correct, but the results can definitely be improved. This form of depth mapping is not among those that produce the highest quality maps (especially being uncalibrated). The biggest improvement will likely come from using a different stereo matching algorithm. The lighting may also be having a significant effect. The right image (at least to my naked eye) appears to be less well lit which could interfere with the reconstruction. You could first try brightening it to the same level as the other, or gather new images if that is possible. From here out, I’ll assume you have no access to the original cameras, so I’ll consider gathering new images, altering the setup, or performing calibration to be out of scope. (If you do have access to the setup and cameras, then I would suggest checking calibration and using a calibrated method as this will work better).

You used StereoBM for calculating your disparity (depth map) which does work, but StereoSGBM is much better suited for this application (it handles smoother edges better). You can see the difference below.

This article explains the differences in more depth:

Block matching focuses on high texture images (think a picture of a tree) and semi-global block matching will focus on sub pixel level matching and pictures with more smooth textures (think a picture of a hallway).

Without any explicit intrinsic camera parameters, specifics about the camera setup (like focal distance, distance between the cameras, distance to the subject, etc.), a known dimension in the image, or motion (to use structure from motion), you can only obtain 3D reconstruction up to a projective transform; you won’t have a sense of scale or necessarily rotation either, but you can still generate a relative depth map. You will likely suffer from some barrel and other distortions which could be removed with proper camera calibration, but you can get reasonable results without it as long as the cameras aren’t terrible (lens system isn’t too distorted) and are set up pretty close to canonical configuration (which basically means they are oriented such that their optical axes are as close to parallel as possible, and their fields of view overlap sufficiently). This doesn’t however appear to be the OPs issue as he did manage to get alright rectified images with the uncalibrated method.

Basic Procedure

  1. Find at least 5 well-matched points in both images you can use to calculate the Fundamental Matrix (you can use any detector and matcher you like, I kept FLANN but used ORB to do detection as SIFT isn’t in the main version of OpenCV for 4.2.0)
  2. Calculate the Fundamental Matrix, F, with findFundamentalMat
  3. Undistort your images with stereoRectifyUncalibrated and warpPerspective
  4. Calculate Disparity (Depth Map) with StereoSGBM

The results are much better:

Matches with ORB and FLANN

Undistorted images (left, then right)


Disparity

StereoBM

This result looks similar to the OPs problems (speckling, gaps, wrong depths in some areas).

StereoSGBM (tuned)

This result looks much better and uses roughly the same method as the OP, minus the final disparity calculation, making me think the OP would see similar improvements on his images, had they been provided.

Post filtering

There’s a good article about this in the OpenCV docs. I’d recommend looking at it if you need really smooth maps.

The example photos above are frame 1 from the scene ambush_2 in the MPI Sintel Dataset.

Full code (Tested on OpenCV 4.2.0):

import cv2
import numpy as np
import matplotlib.pyplot as plt

imgL = cv2.imread("tsukuba_l.png", cv2.IMREAD_GRAYSCALE)  # left image
imgR = cv2.imread("tsukuba_r.png", cv2.IMREAD_GRAYSCALE)  # right image


def get_keypoints_and_descriptors(imgL, imgR):
    """Use ORB detector and FLANN matcher to get keypoints, descritpors,
    and corresponding matches that will be good for computing
    homography.
    """
    orb = cv2.ORB_create()
    kp1, des1 = orb.detectAndCompute(imgL, None)
    kp2, des2 = orb.detectAndCompute(imgR, None)

    ############## Using FLANN matcher ##############
    # Each keypoint of the first image is matched with a number of
    # keypoints from the second image. k=2 means keep the 2 best matches
    # for each keypoint (best matches = the ones with the smallest
    # distance measurement).
    FLANN_INDEX_LSH = 6
    index_params = dict(
        algorithm=FLANN_INDEX_LSH,
        table_number=6,  # 12
        key_size=12,  # 20
        multi_probe_level=1,
    )  # 2
    search_params = dict(checks=50)  # or pass empty dictionary
    flann = cv2.FlannBasedMatcher(index_params, search_params)
    flann_match_pairs = flann.knnMatch(des1, des2, k=2)
    return kp1, des1, kp2, des2, flann_match_pairs


def lowes_ratio_test(matches, ratio_threshold=0.6):
    """Filter matches using the Lowe's ratio test.

    The ratio test checks if matches are ambiguous and should be
    removed by checking that the two distances are sufficiently
    different. If they are not, then the match at that keypoint is
    ignored.

    https://stackoverflow.com/questions/51197091/how-does-the-lowes-ratio-test-work
    """
    filtered_matches = []
    for m, n in matches:
        if m.distance < ratio_threshold * n.distance:
            filtered_matches.append(m)
    return filtered_matches


def draw_matches(imgL, imgR, kp1, des1, kp2, des2, flann_match_pairs):
    """Draw the first 8 mathces between the left and right images."""
    # https://docs.opencv.org/4.2.0/d4/d5d/group__features2d__draw.html
    # https://docs.opencv.org/2.4/modules/features2d/doc/common_interfaces_of_descriptor_matchers.html
    img = cv2.drawMatches(
        imgL,
        kp1,
        imgR,
        kp2,
        flann_match_pairs[:8],
        None,
        flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS,
    )
    cv2.imshow("Matches", img)
    cv2.imwrite("ORB_FLANN_Matches.png", img)
    cv2.waitKey(0)


def compute_fundamental_matrix(matches, kp1, kp2, method=cv2.FM_RANSAC):
    """Use the set of good mathces to estimate the Fundamental Matrix.

    See  https://en.wikipedia.org/wiki/Eight-point_algorithm#The_normalized_eight-point_algorithm
    for more info.
    """
    pts1, pts2 = [], []
    fundamental_matrix, inliers = None, None
    for m in matches[:8]:
        pts1.append(kp1[m.queryIdx].pt)
        pts2.append(kp2[m.trainIdx].pt)
    if pts1 and pts2:
        # You can play with the Threshold and confidence values here
        # until you get something that gives you reasonable results. I
        # used the defaults
        fundamental_matrix, inliers = cv2.findFundamentalMat(
            np.float32(pts1),
            np.float32(pts2),
            method=method,
            # ransacReprojThreshold=3,
            # confidence=0.99,
        )
    return fundamental_matrix, inliers, pts1, pts2


############## Find good keypoints to use ##############
kp1, des1, kp2, des2, flann_match_pairs = get_keypoints_and_descriptors(imgL, imgR)
good_matches = lowes_ratio_test(flann_match_pairs, 0.2)
draw_matches(imgL, imgR, kp1, des1, kp2, des2, good_matches)


############## Compute Fundamental Matrix ##############
F, I, points1, points2 = compute_fundamental_matrix(good_matches, kp1, kp2)


############## Stereo rectify uncalibrated ##############
h1, w1 = imgL.shape
h2, w2 = imgR.shape
thresh = 0
_, H1, H2 = cv2.stereoRectifyUncalibrated(
    np.float32(points1), np.float32(points2), F, imgSize=(w1, h1), threshold=thresh,
)

############## Undistort (Rectify) ##############
imgL_undistorted = cv2.warpPerspective(imgL, H1, (w1, h1))
imgR_undistorted = cv2.warpPerspective(imgR, H2, (w2, h2))
cv2.imwrite("undistorted_L.png", imgL_undistorted)
cv2.imwrite("undistorted_R.png", imgR_undistorted)

############## Calculate Disparity (Depth Map) ##############

# Using StereoBM
stereo = cv2.StereoBM_create(numDisparities=16, blockSize=15)
disparity_BM = stereo.compute(imgL_undistorted, imgR_undistorted)
plt.imshow(disparity_BM, "gray")
plt.colorbar()
plt.show()

# Using StereoSGBM
# Set disparity parameters. Note: disparity range is tuned according to
#  specific parameters obtained through trial and error.
win_size = 2
min_disp = -4
max_disp = 9
num_disp = max_disp - min_disp  # Needs to be divisible by 16
stereo = cv2.StereoSGBM_create(
    minDisparity=min_disp,
    numDisparities=num_disp,
    blockSize=5,
    uniquenessRatio=5,
    speckleWindowSize=5,
    speckleRange=5,
    disp12MaxDiff=2,
    P1=8 * 3 * win_size ** 2,
    P2=32 * 3 * win_size ** 2,
)
disparity_SGBM = stereo.compute(imgL_undistorted, imgR_undistorted)
plt.imshow(disparity_SGBM, "gray")
plt.colorbar()
plt.show()


回答 1

可能存在几个导致质量低下的问题Depth ChannelDisparity Channel导致我们产生质量低下的立体声序列。以下是其中的6个问题:

可能的问题我

  • 公式不完整

作为一个词uncalibrated意味着,stereoRectifyUncalibrated实例方法计算整改转化为你,如果你不知道或者不知道您的立体声对,并在环境中的相对位置的内部参数。

cv.StereoRectifyUncalibrated(pts1, pts2, fm, imgSize, rhm1, rhm2, thres)

哪里:

# pts1    –> an array of feature points in a first camera
# pts2    –> an array of feature points in a first camera
# fm      –> input fundamental matrix
# imgSize -> size of an image
# rhm1    -> output rectification homography matrix for a first image
# rhm2    -> output rectification homography matrix for a second image
# thres   –> optional threshold used to filter out outliers

您的方法看起来像这样:

cv2.StereoRectifyUncalibrated(p1fNew, p2fNew, F, (2048, 2048))

所以,你不要考虑三个参数:rhm1rhm2thres。如果为a threshold > 0,则在计算单应性之前会拒绝所有不符合极线几何的点对。否则,所有点均视为内点。该公式如下所示:

(pts2[i]^t * fm * pts1[i]) > thres

# t   –> translation vector between coordinate systems of cameras

因此,我认为由于公式计算不完整,可能会出现视觉错误。

您可以在官方资源上阅读相机校准和3D重建


可能的问题二

  • 轴间距离

interaxial distance左右相机镜头之间必须牢固not greater than 200 mm。当interaxial distance大于interocular距离时,这种效果称为hyperstereoscopyhyperdivergence,不仅导致场景中的深度夸张,而且导致观看者的身体不便。阅读Autodesk的“立体电影制作白皮书”以了解有关此主题的更多信息。


可能的问题三

  • 平行摄影机与Toed-In摄影机模式

Disparity Map由于不正确的相机模式计算,可能会导致视觉上的误差。许多立体学家更喜欢,Toe-In camera mode但例如皮克斯(Pixar)更喜欢Parallel camera mode


可能的问题四

  • 垂直对齐

在立体视觉中,如果发生垂直偏移(即使其中一个视图向上偏移1毫米),也会破坏稳固的立体声体验。因此,在生成Disparity Map音频之前,必须确保立体声对的左右视图已相应对齐。请参阅Technicolor立体镜白皮书,了解立体声方面的15个常见问题。

立体声整流矩阵:

   ┌                  ┐
   |  f   0   cx  tx  |
   |  0   f   cy  ty  |   # use "ty" value to fix vertical shift in one image
   |  0   0   1   0   |
   └                  ┘

这是一个StereoRectify方法:

cv.StereoRectify(cameraMatrix1, cameraMatrix2, distCoeffs1, distCoeffs2, imageSize, R, T, R1, R2, P1, P2, Q=None, flags=CV_CALIB_ZERO_DISPARITY, alpha=-1, newImageSize=(0, 0)) -> (roi1, roi2)


可能的问题五

  • 镜头变形

镜头失真是立体声合成中非常重要的主题。在生成之前,Disparity Map您需要先取消左右视图的失真,然后再生成视差通道,然后再次对这两个视图进行重新扭曲。


可能的问题六

  • 低质量深度通道,无抗锯齿

为了创建高质量的图像,Disparity Map您需要左右Depth Channels必须预先生成。在3D封装中工作时,只需单击一下即可渲染高质量的深度通道(边缘清晰)。但是从视频序列中生成高质量的深度通道并不容易,因为立体声对必须在您的环境中移动才能为将来的深度运动算法生成初始数据。如果帧中没有运动,则深度通道将非常差。

此外,Depth通道本身还有另一个缺点–因为的边缘没有抗锯齿,所以的边缘与RGB的边缘不匹配。


视差渠道代码段:

在这里,我想代表一种生成的快速方法Disparity Map

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

imageLeft = cv.imread('paris_left.png', 0)
imageRight = cv.imread('paris_right.png', 0)
stereo = cv.StereoBM_create(numDisparities=16, blockSize=15)
disparity = stereo.compute(imageLeft, imageRight)
plt.imshow(disparity, 'gray')
plt.show()

There might be several possible issues resulting in low-quality Depth Channel and Disparity Channel what leads us to low-quality stereo sequence. Here are 6 of those issues:

Possible issue I

  • Incomplete Formula

As a word uncalibrated implies, stereoRectifyUncalibrated instance method calculates a rectification transformations for you, in case you don’t know or can’t know intrinsic parameters of your stereo pair and its relative position in the environment.

cv.StereoRectifyUncalibrated(pts1, pts2, fm, imgSize, rhm1, rhm2, thres)

where:

# pts1    –> an array of feature points in a first camera
# pts2    –> an array of feature points in a first camera
# fm      –> input fundamental matrix
# imgSize -> size of an image
# rhm1    -> output rectification homography matrix for a first image
# rhm2    -> output rectification homography matrix for a second image
# thres   –> optional threshold used to filter out outliers

And your method looks this way:

cv2.StereoRectifyUncalibrated(p1fNew, p2fNew, F, (2048, 2048))

So, you do not take into account three parameters: rhm1, rhm2 and thres. If a threshold > 0, all point pairs that don’t comply with a epipolar geometry are rejected prior to computing the homographies. Otherwise, all points are considered inliers. This formula looks like this:

(pts2[i]^t * fm * pts1[i]) > thres

# t   –> translation vector between coordinate systems of cameras

Thus, I believe that visual inaccuracies might appear due to an incomplete formula’s calculation.

You can read Camera Calibration and 3D Reconstruction on official resource.


Possible issue II

  • Interaxial Distance

A robust interaxial distance between left and right camera lenses must be not greater than 200 mm. When the interaxial distance is larger than the interocular distance, the effect is called hyperstereoscopy or hyperdivergence and results not only in depth exaggeration in the scene but also in viewer’s physical inconvenience. Read Autodesk’s Stereoscopic Filmmaking Whitepaper to find out more on this topic.


Possible issue III

  • Parallel vs Toed-In camera mode

Visual inaccuracies in resulted Disparity Map may occur due to incorrect Camera Mode calculation. Many stereographers prefer Toe-In camera mode but Pixar, for example, prefers Parallel camera mode.


Possible issue IV

  • Vertical Alignment

In stereoscopy, if a vertical shift occurs (even if one of the views is shifted up by 1 mm) it ruins a robust stereo experience. So, before generating Disparity Map you must be sure that left and right views of your stereo pair are accordingly aligned. Look at Technicolor Sterreoscopic Whitepaper about 15 common problems in stereo.

Stereo Rectification Matrix:

   ┌                  ┐
   |  f   0   cx  tx  |
   |  0   f   cy  ty  |   # use "ty" value to fix vertical shift in one image
   |  0   0   1   0   |
   └                  ┘

Here’s a StereoRectify method:

cv.StereoRectify(cameraMatrix1, cameraMatrix2, distCoeffs1, distCoeffs2, imageSize, R, T, R1, R2, P1, P2, Q=None, flags=CV_CALIB_ZERO_DISPARITY, alpha=-1, newImageSize=(0, 0)) -> (roi1, roi2)


Possible issue V

  • Lens Distortion

Lens Distortion is very important topic in stereo composition. Before generating a Disparity Map you need to undistort left and right views, after this generate a disparity channel, and then redistort both views again.


Possible issue VI

  • Low-quality Depth channel without anti-aliasing

For creating a high-quality Disparity Map you need left and right Depth Channels that must be pre-generated. When you work in 3D package you can render a high-quality Depth Channel (with crisp edges) with just one click. But generating a high-quality depth channel from video sequence is not easy because stereo pair has to move in your environment for producing an initial data for future depth-from-motion algorithm. If there’s no motion in a frame a depth channel will be extremely poor.

Also, Depth channel itself has one more drawback – its edges do not match the edges of the RGB because it has no anti-aliasing.


Disparity channel code snippet:

Here I’d like to represent a quick approach to generate a Disparity Map:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

imageLeft = cv.imread('paris_left.png', 0)
imageRight = cv.imread('paris_right.png', 0)
stereo = cv.StereoBM_create(numDisparities=16, blockSize=15)
disparity = stereo.compute(imageLeft, imageRight)
plt.imshow(disparity, 'gray')
plt.show()


Django:为什么某些模型字段会相互冲突?

问题:Django:为什么某些模型字段会相互冲突?

我想创建一个包含2个指向用户的链接的对象。例如:

class GameClaim(models.Model):
    target = models.ForeignKey(User)
    claimer = models.ForeignKey(User)
    isAccepted = models.BooleanField()

但是运行服务器时出现以下错误:

  • 字段“目标”的访问器与相关字段“ User.gameclaim_set”冲突。在’target’的定义中添加related_name参数。

  • 字段“ claimer”的访问器与相关字段“ User.gameclaim_set”冲突。在“ claimer”的定义中添加一个related_name参数。

您能否解释为什么我会收到错误以及如何解决这些错误?

I want to create an object that contains 2 links to Users. For example:

class GameClaim(models.Model):
    target = models.ForeignKey(User)
    claimer = models.ForeignKey(User)
    isAccepted = models.BooleanField()

but I am getting the following errors when running the server:

  • Accessor for field ‘target’ clashes with related field ‘User.gameclaim_set’. Add a related_name argument to the definition for ‘target’.

  • Accessor for field ‘claimer’ clashes with related field ‘User.gameclaim_set’. Add a related_name argument to the definition for ‘claimer’.

Can you please explain why I am getting the errors and how to fix them?


回答 0

您有两个用户外键。Django自动创建一个从User到GameClaim的反向关系,通常是gameclaim_set。但是,由于您有两个FK,因此将具有两个gameclaim_set属性,这显然是不可能的。因此,您需要告诉Django用于反向关系的名称。

使用related_nameFK定义中的属性。例如

class GameClaim(models.Model):
    target = models.ForeignKey(User, related_name='gameclaim_targets')
    claimer = models.ForeignKey(User, related_name='gameclaim_users')
    isAccepted = models.BooleanField()

You have two foreign keys to User. Django automatically creates a reverse relation from User back to GameClaim, which is usually gameclaim_set. However, because you have two FKs, you would have two gameclaim_set attributes, which is obviously impossible. So you need to tell Django what name to use for the reverse relation.

Use the related_name attribute in the FK definition. e.g.

class GameClaim(models.Model):
    target = models.ForeignKey(User, related_name='gameclaim_targets')
    claimer = models.ForeignKey(User, related_name='gameclaim_users')
    isAccepted = models.BooleanField()

回答 1

User模型试图创建具有相同名称的两个领域,一个是GameClaims说有User作为target,而另一个用于GameClaims该有User作为claimer。这是上的文档related_name,这是Django允许您设置属性名称的方法,以便自动生成的属性不会冲突。

The User model is trying to create two fields with the same name, one for the GameClaims that have that User as the target, and another for the GameClaims that have that User as the claimer. Here’s the docs on related_name, which is Django’s way of letting you set the names of the attributes so the autogenerated ones don’t conflict.


回答 2

OP没有使用抽象基类…但是,如果您使用的是,您会发现在FK中硬编码related_name(例如…,related_name =“ myname”)会导致许多此类冲突错误-从基类继承的每个类一个。下面提供的链接包含解决方法,这很简单,但绝对不明显。

从Django文档…

如果在ForeignKey或ManyToManyField上使用related_name属性,则必须始终为该字段指定唯一的反向名称。这通常会在抽象基类中引起问题,因为此类的字段包含在每个子类中,并且每次属性(包括related_name)的值都完全相同。

更多信息在这里

The OP isn’t using a abstract base class… but if you are, you will find that hard coding the related_name in the FK (e.g. …, related_name=”myname”) will result in a number of these conflict errors – one for each inherited class from the base class. The link provided below contains the workaround, which is simple, but definitely not obvious.

From the django docs…

If you are using the related_name attribute on a ForeignKey or ManyToManyField, you must always specify a unique reverse name for the field. This would normally cause a problem in abstract base classes, since the fields on this class are included into each of the child classes, with exactly the same values for the attributes (including related_name) each time.

More info here.


回答 3

有时,您related_name 实际上必须在使用继承的任何时候使用额外的格式设置。

class Value(models.Model):
    value = models.DecimalField(decimal_places=2, max_digits=5)
    animal = models.ForeignKey(
        Animal, related_name="%(app_label)s_%(class)s_related")

    class Meta:
        abstract = True

class Height(Value):
    pass

class Weigth(Value):
    pass

class Length(Value):
    pass

这里没有冲突,但是related_name定义一次,Django将小心创建唯一的关系名称。

然后在Value类的子级中,您可以访问:

herdboard_height_related
herdboard_lenght_related
herdboard_weight_related

Sometimes you have to use extra formatting in related_name – actually, any time when inheritance is used.

class Value(models.Model):
    value = models.DecimalField(decimal_places=2, max_digits=5)
    animal = models.ForeignKey(
        Animal, related_name="%(app_label)s_%(class)s_related")

    class Meta:
        abstract = True

class Height(Value):
    pass

class Weigth(Value):
    pass

class Length(Value):
    pass

No clash here, but related_name is defined once and Django will take care for creating unique relation names.

then in children of Value class, you’ll have access to:

herdboard_height_related
herdboard_lenght_related
herdboard_weight_related

回答 4

当我将子模块作为应用程序添加到Django项目时,似乎偶尔会遇到这种情况,例如,给定以下结构:

myapp/
myapp/module/
myapp/module/models.py

如果我将以下内容添加到INSTALLED_APPS:

'myapp',
'myapp.module',

Django似乎两次处理了myapp.mymodule models.py文件,并抛出了以上错误。可以通过在INSTALLED_APPS列表中不包括主模块来解决此问题:

'myapp.module',

包含myapp代替myapp.module会导致所有数据库表都使用不正确的名称创建,因此这似乎是正确的方法。

我在寻找此问题的解决方案时遇到了这篇文章,所以我想把它放在这里:)

I seem to come across this occasionally when I add a submodule as an application to a django project, for example given the following structure:

myapp/
myapp/module/
myapp/module/models.py

If I add the following to INSTALLED_APPS:

'myapp',
'myapp.module',

Django seems to process the myapp.mymodule models.py file twice and throws the above error. This can be resolved by not including the main module in the INSTALLED_APPS list:

'myapp.module',

Including the myapp instead of myapp.module causes all the database tables to be created with incorrect names, so this seems to be the correct way to do it.

I came across this post while looking for a solution to this problem so figured I’d put this here :)


回答 5

只是添加约旦的答案(感谢约旦的提示),如果您在应用程序上方导入级别,然后导入应用程序,则也可能发生这种情况,例如

myproject/ apps/ foo_app/ bar_app/

因此,如果要导入应用程序foo_app和bar_app,则可能会遇到此问题。我的应用程序foo_app和bar_app都列在设置中。INSTALLED_APPS

而且您还是想避免导入应用程序,因为那样您会将相同的应用程序安装在2个不同的命名空间中

apps.foo_appfoo_app

Just adding to Jordan’s answer (thanks for the tip Jordan) it can also happen if you import the level above the apps and then import the apps e.g.

myproject/ apps/ foo_app/ bar_app/

So if you are importing apps, foo_app and bar_app then you could get this issue. I had apps, foo_app and bar_app all listed in settings.INSTALLED_APPS

And you want to avoid importing apps anyway, because then you have the same app installed in 2 different namespaces

apps.foo_app and foo_app


如何在python中使用当前日期和时间创建文件名?

问题:如何在python中使用当前日期和时间创建文件名?

这是一个功能代码(成功创建文件)

sys.stdout = open('filename1.xml', 'w')

现在,我正在尝试使用当前日期时间来命名文件(我不是python的专家)

filename1 = datetime.now().strftime("%Y%m%d-%H%M%S")
sys.stdout = open(filename1 + '.xml', 'w')

我想写出一个具有确切日期和时间的文件名,它是程序已经创建的xml文件,我只需要命名该文件即可。上面的代码不起作用。

[编辑] -返回错误

  File "./fix.py", line 226, in <module>
    filenames = datetime.now().strftime("%Y%m%d-%H%M%S")
AttributeError: 'module' object has no attribute 'now'

Here is a functional code (Create file with success)

sys.stdout = open('filename1.xml', 'w')

Now I’m trying to name the file with the current Date Time (I’m not an expert in python)

filename1 = datetime.now().strftime("%Y%m%d-%H%M%S")
sys.stdout = open(filename1 + '.xml', 'w')

I want to write out a file name with the exact date and time, it is a xml file, that the program has already create, I just need to name the file. The above code is not working.

[EDITED] – The error returned

  File "./fix.py", line 226, in <module>
    filenames = datetime.now().strftime("%Y%m%d-%H%M%S")
AttributeError: 'module' object has no attribute 'now'

回答 0

不使用时datetime,这可以解决您的问题(回答您的问题),该问题是使用您指定的当前时间和日期格式获取字符串:

import time
timestr = time.strftime("%Y%m%d-%H%M%S")
print timestr

Yield:

20120515-155045

因此您的文件名可以追加或使用此字符串。

While not using datetime, this solves your problem (answers your question) of getting a string with the current time and date format you specify:

import time
timestr = time.strftime("%Y%m%d-%H%M%S")
print timestr

yields:

20120515-155045

so your filename could append or use this string.


回答 1

nowdatetime模块中类中的类方法datetime。所以你需要

datetime.datetime.now()

或者您可以使用其他导入

from datetime import datetime

通过这种方式,您可以datetime.now按照问题中的代码使用。

now is a class method in the class datetime in the module datetime. So you need

datetime.datetime.now()

Or you can use a different import

from datetime import datetime

Done this way allows you to use datetime.now as per the code in the question.


回答 2

更改此行

filename1 = datetime.now().strftime("%Y%m%d-%H%M%S")

filename1 = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")

注意额外的datetime。或者,将您更改 import datetimefrom datetime import datetime

Change this line

filename1 = datetime.now().strftime("%Y%m%d-%H%M%S")

To

filename1 = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")

Note the extra datetime. Alternatively, change your import datetime to from datetime import datetime


回答 3

这个更容易被人类理解。

from datetime import datetime

datetime.now().strftime("%d-%m-%Y_%I-%M-%S_%p")
'15-08-2019_11-57-48_PM'

This one is much more human readable.

from datetime import datetime

datetime.now().strftime("%Y_%m_%d-%I_%M_%S_%p")
'2020_08_12-03_29_22_AM'

回答 4

我很惊讶没有单个格式化程序返回默认的(并且安全的)“用于追加文件名”-时间的格式,我们可以简单地编写 FD.write('mybackup'+time.strftime('%(formatter here)') + 'ext'

"%x" instead of "%Y%m%d-%H%M%S"

I’m surprised there is not some single formatter that returns a default (and safe) ‘for appending in filename’ – format of the time, We could simply write FD.write('mybackup'+time.strftime('%(formatter here)') + 'ext'

"%x" instead of "%Y%m%d-%H%M%S"

回答 5

我需要在文件夹名称中包括日期时间戳,以便从Web刮板转储文件。

# import time and OS modules to use to build file folder name
import time
import os 

# Build string for directory to hold files
# Output Configuration
#   drive_letter = Output device location (hard drive) 
#   folder_name = directory (folder) to receive and store PDF files

drive_letter = r'D:\\' 
folder_name = r'downloaded-files'
folder_time = datetime.now().strftime("%Y-%m-%d_%I-%M-%S_%p")
folder_to_save_files = drive_letter + folder_name + folder_time 

# IF no such folder exists, create one automatically
if not os.path.exists(folder_to_save_files):
    os.mkdir(folder_to_save_files)

Here’s some that I needed to include the date-time stamp in the folder name for dumping files from a web scraper.

# import time and OS modules to use to build file folder name
import time
import os 

# Build string for directory to hold files
# Output Configuration
#   drive_letter = Output device location (hard drive) 
#   folder_name = directory (folder) to receive and store PDF files

drive_letter = r'D:\\' 
folder_name = r'downloaded-files'
folder_time = datetime.now().strftime("%Y-%m-%d_%I-%M-%S_%p")
folder_to_save_files = drive_letter + folder_name + folder_time 

# IF no such folder exists, create one automatically
if not os.path.exists(folder_to_save_files):
    os.mkdir(folder_to_save_files)

尽管我调用pyplot.show(),但matplotlib不会显示我的绘图

问题:尽管我调用pyplot.show(),但matplotlib不会显示我的绘图

matplotlib上需要帮助。是的,我没有忘记调用pyplot.show()。

$ ipython –pylab

import matplotlib.pyplot as p 
p.plot(range(20), range(20))

matplotlib.lines.Line2D at 0xade2b2c作为输出返回。

p.show()

没事了 没有错误讯息。没有新窗口。没有。我matplotlib使用pip进行安装,但未收到任何错误消息。

细节:

我用,

  • 的Ubuntu
  • IPython v0.11
  • Python v2.6.6
  • matplotlib v1.0.1

Help required on matplotlib. Yes, I did not forget calling the pyplot.show().

$ ipython –pylab

import matplotlib.pyplot as p 
p.plot(range(20), range(20))

It returns matplotlib.lines.Line2D at 0xade2b2c as the output.

p.show()

There is nothing to happen. No error message. No new window. Nothing. I install matplotlib by using pip and I didn’t take any error messages.

Details:

I use,

  • Ubuntu
  • IPython v0.11
  • Python v2.6.6
  • matplotlib v1.0.1

回答 0

如果将后端设置为template~/.matplotlib/matplotlibrc,则可以重现您的症状:

〜/ .matplotlib / matplotlibrc:

# backend      : GtkAgg
backend      : template

请注意,该文件matplotlibrc可能不在目录中~/.matplotlib/。在这种情况下,以下代码显示其位置:

>>> import matplotlib
>>> matplotlib.matplotlib_fname()

In [1]: import matplotlib.pyplot as p

In [2]: p.plot(range(20),range(20))
Out[2]: [<matplotlib.lines.Line2D object at 0xa64932c>]

In [3]: p.show()

如果您~/.matplotlib/matplotlibrc将后端编辑为,并将其更改为GtkAgg,则应该会看到一个图。您可以使用以下命令列出计算机上所有可用的后端

import matplotlib.rcsetup as rcsetup
print(rcsetup.all_backends)

它应该返回类似以下的列表:

['GTK', 'GTKAgg', 'GTKCairo', 'FltkAgg', 'MacOSX', 'QtAgg', 'Qt4Agg',
'TkAgg', 'WX', 'WXAgg', 'CocoaAgg', 'agg', 'cairo', 'emf', 'gdk', 'pdf',
'ps', 'svg', 'template']

参考:

If I set my backend to template in ~/.matplotlib/matplotlibrc, then I can reproduce your symptoms:

~/.matplotlib/matplotlibrc:

# backend      : GtkAgg
backend      : template

Note that the file matplotlibrc may not be in directory ~/.matplotlib/. In this case, the following code shows where it is:

>>> import matplotlib
>>> matplotlib.matplotlib_fname()

In [1]: import matplotlib.pyplot as p

In [2]: p.plot(range(20),range(20))
Out[2]: [<matplotlib.lines.Line2D object at 0xa64932c>]

In [3]: p.show()

If you edit ~/.matplotlib/matplotlibrc and change the backend to something like GtkAgg, you should see a plot. You can list all the backends available on your machine with

import matplotlib.rcsetup as rcsetup
print(rcsetup.all_backends)

It should return a list like:

['GTK', 'GTKAgg', 'GTKCairo', 'FltkAgg', 'MacOSX', 'QtAgg', 'Qt4Agg',
'TkAgg', 'WX', 'WXAgg', 'CocoaAgg', 'agg', 'cairo', 'emf', 'gdk', 'pdf',
'ps', 'svg', 'template']

Reference:


回答 1

我在Ubuntu 12.04上遇到了完全相同的问题,因为我使用以下命令安装了matplotlib(在virtualenv中)

pip install matplotlib

长话短说,我的建议是:不要尝试使用pip或手工安装matplotlib;让真正的软件包管理器(例如apt-get / synaptic)为您安装它及其所有依赖项。

不幸的是,matplotlib的后端(用于实际绘制图的替代方法)具有pip无法处理的各种依赖关系。更糟糕的是,它无声地失败了。也就是说,pip install matplotlib似乎成功安装了matplotlib。但是,当您尝试使用它时(例如pyplot.show()),将不会出现绘图窗口。我尝试了网络上人们建议的所有不同后端(Qt4Agg,GTK等),但它们都失败了(即,当我尝试导入matplotlib.pyplot时,我得到了,ImportError因为它试图导入缺少的某些依赖项)。然后,我研究了如何安装这些依赖项,但这只是让我想放弃使用pip(在virtualenv内)作为任何具有非Python软件包依赖项的软件包的可行安装解决方案。

整个经历使我爬回apt-get / synaptic(即Ubuntu软件包管理器)来安装matplotlib之类的软件。那很好。当然,这意味着您只能安装到您的系统目录中,没有virtualenv的好处,并且您受困于Ubuntu发行的版本,这可能落后于当前版本…

I ran into the exact same problem on Ubuntu 12.04, because I installed matplotlib (within a virtualenv) using

pip install matplotlib

To make long story short, my advice is: don’t try to install matplotlib using pip or by hand; let a real package manager (e.g. apt-get / synaptic) install it and all its dependencies for you.

Unfortunately, matplotlib’s backends (alternative methods for actually rendering your plots) have all sorts of dependencies that pip will not deal with. Even worse, it fails silently; that is, pip install matplotlib appears to install matplotlib successfully. But when you try to use it (e.g. pyplot.show()), no plot window will appear. I tried all the different backends that people on the web suggest (Qt4Agg, GTK, etc.), and they all failed (i.e. when I tried to import matplotlib.pyplot, I get ImportError because it’s trying to import some dependency that’s missing). I then researched how to install those dependencies, but it just made me want to give up using pip (within virtualenv) as a viable installation solution for any package that has non-Python package dependencies.

The whole experience sent me crawling back to apt-get / synaptic (i.e. the Ubuntu package manager) to install software like matplotlib. That worked perfectly. Of course, that means you can only install into your system directories, no virtualenv goodness, and you are stuck with the versions that Ubuntu distributes, which may be way behind the current version…


回答 2

%matplotlib内联

对于使用笔记本的我来说,在绘图工作之前添加以上行。

%matplotlib inline

For me working with notebook, adding the above line before the plot works.


回答 3

备查,

我遇到了同样的问题-pylab没有在ipython下显示。通过更改ipython的配置文件{ipython_config.py}已解决了该问题。在配置文件中

c.InteractiveShellApp.pylab = 'auto'

我将’auto’更改为’qt’,现在我看到了图表

For future reference,

I have encountered the same problem — pylab was not showing under ipython. The problem was fixed by changing ipython’s config file {ipython_config.py}. In the config file

c.InteractiveShellApp.pylab = 'auto'

I changed ‘auto’ to ‘qt’ and now I see graphs


回答 4

只需输入:

plt.ion()

请在23:30 参见https://www.youtube.com/watch?v=1zmV8lZsHF4

plt使用是因为我的导入:import matplotlib.pyplotas plt

我在带有iTerm2的Mac上使用python2.7。

Just type:

plt.ion()

See https://www.youtube.com/watch?v=1zmV8lZsHF4 at 23:30 !

plt is used because of my import: import matplotlib.pyplot as plt

I’m using python2.7 on a mac with iTerm2.


回答 5

解决我问题的方法只是在顶部的ipython Notebook中使用以下两行

%matplotib inline
%pylab inline

而且有效。我正在使用Ubuntu16.04和ipython-5.1

What solved my problem was just using the below two lines in ipython notebook at the top

%matplotib inline
%pylab inline

And it worked. I’m using Ubuntu16.04 and ipython-5.1


回答 6

我必须从源代码安装matplotlib才能使它工作。关键说明(来自http://www.pyimagesearch.com/2015/08/24/resolved-matplotlib-figures-not-showing-up-or-displaying/)为:

$ workon plotting
$ pip uninstall matplotlib
$ git clone https://github.com/matplotlib/matplotlib.git
$ cd matplotlib
$ python setup.py install

正如@unutbu所说,通过更改后端,我遇到了很多问题,所有不同的后端都不起作用。

I had to install matplotlib from source to get this to work. The key instructions (from http://www.pyimagesearch.com/2015/08/24/resolved-matplotlib-figures-not-showing-up-or-displaying/) are:

$ workon plotting
$ pip uninstall matplotlib
$ git clone https://github.com/matplotlib/matplotlib.git
$ cd matplotlib
$ python setup.py install

By changing the backend, as @unutbu says, I just ran into loads more problems with all the different backends not working either.


回答 7

在导入pylab之前添加以下两行似乎对我有用

import matplotlib
matplotlib.use("gtk")

import sys
import pylab
import numpy as np

Adding the following two lines before importing pylab seems to work for me

import matplotlib
matplotlib.use("gtk")

import sys
import pylab
import numpy as np

回答 8

确保启用此启动脚本:(“首选项”>“控制台”>“高级选项”)

/usr/lib/python2.7/dist-packages/spyderlib/scientific_startup.py

如果启用了标准的PYTHONSTARTUP,则不会有交互式绘图

Be sure to have this startup script enabled : ( Preferences > Console > Advanced Options )

/usr/lib/python2.7/dist-packages/spyderlib/scientific_startup.py

If the standard PYTHONSTARTUP is enabled you won’t have an interactive plot


回答 9

类似@Rikki,我通过升级解决了这个问题matplotlibpip install matplotlib --upgrade。如果无法升级,则可以卸载并重新安装。

pip uninstall matplotlib
pip install matplotlib

Similar to @Rikki, I solved this problem by upgrading matplotlib with pip install matplotlib --upgrade. If you can’t upgrade uninstalling and reinstalling may work.

pip uninstall matplotlib
pip install matplotlib

回答 10

对我来说,如果我只是在macOS 下创建一个 matplotlibrc文件,就会发生问题~/.matplotlib。在其中添加“后端:macosx”可解决此问题。

我认为这是一个错误:如果backend未在我的代码中指定,matplotlibrc则应采用默认值。

For me the problem happens if I simply create an empty matplotlibrc file under ~/.matplotlib on macOS. Adding “backend: macosx” in it fixes the problem.

I think it is a bug: if backend is not specified in my matplotlibrc it should take the default value.


回答 11

运行代码后,包括:

import pylab as p
p.show()

After running your code include:

import pylab as p
p.show()

回答 12

我发现我需要window = Tk(),然后window.mainloop()

I found that I needed window = Tk() and then window.mainloop()


回答 13

对于Ubuntu 12.04:

sudo apt-get install python-qt4
virtualenv .env --no-site-packages
source .env/bin/activate
easy_install -U distribute
ln -s /usr/lib/python2.7/dist-packages/PyQt4 .
ln -s /usr/lib/python2.7/dist-packages/sip.so .
pip install matplotlib

For Ubuntu 12.04:

sudo apt-get install python-qt4
virtualenv .env --no-site-packages
source .env/bin/activate
easy_install -U distribute
ln -s /usr/lib/python2.7/dist-packages/PyQt4 .
ln -s /usr/lib/python2.7/dist-packages/sip.so .
pip install matplotlib

切片NumPy 2d数组,或者如何从nxn数组(n> m)中提取mxm子矩阵?

问题:切片NumPy 2d数组,或者如何从nxn数组(n> m)中提取mxm子矩阵?

我想切片一个NumPy nxn数组。我想提取该数组的m行和列的任意选择(即,行/列数中没有任何模式),使其成为一个新的mxm数组。对于此示例,假设数组为4×4,我想从中提取2×2数组。

这是我们的数组:

from numpy import *
x = range(16)
x = reshape(x,(4,4))

print x
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]

要删除的行和列相同。最简单的情况是,当我想提取在开始或结尾处的2×2子矩阵时,即:

In [33]: x[0:2,0:2]
Out[33]: 
array([[0, 1],
       [4, 5]])

In [34]: x[2:,2:]
Out[34]: 
array([[10, 11],
       [14, 15]])

但是,如果我需要删除其他混合的行/列怎么办?如果我需要删除第一行和第三行/行,从而提取子矩阵,该[[5,7],[13,15]]怎么办?行/线可以有任何组成。我读到某个地方,我只需要使用行/列的索引数组/索引列表来索引我的数组,但这似乎不起作用:

In [35]: x[[1,3],[1,3]]
Out[35]: array([ 5, 15])

我找到了一种方法,即:

    In [61]: x[[1,3]][:,[1,3]]
Out[61]: 
array([[ 5,  7],
       [13, 15]])

第一个问题是,尽管我可以接受,但很难阅读。如果有人有更好的解决方案,我当然想听听。

另一件事是我在一个论坛上读到用数组索引数组会迫使NumPy复制所需的数组,因此在处理大型数组时,这可能会成为问题。为什么这样/这个机制如何运作?

I want to slice a NumPy nxn array. I want to extract an arbitrary selection of m rows and columns of that array (i.e. without any pattern in the numbers of rows/columns), making it a new, mxm array. For this example let us say the array is 4×4 and I want to extract a 2×2 array from it.

Here is our array:

from numpy import *
x = range(16)
x = reshape(x,(4,4))

print x
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]

The line and columns to remove are the same. The easiest case is when I want to extract a 2×2 submatrix that is at the beginning or at the end, i.e. :

In [33]: x[0:2,0:2]
Out[33]: 
array([[0, 1],
       [4, 5]])

In [34]: x[2:,2:]
Out[34]: 
array([[10, 11],
       [14, 15]])

But what if I need to remove another mixture of rows/columns? What if I need to remove the first and third lines/rows, thus extracting the submatrix [[5,7],[13,15]]? There can be any composition of rows/lines. I read somewhere that I just need to index my array using arrays/lists of indices for both rows and columns, but that doesn’t seem to work:

In [35]: x[[1,3],[1,3]]
Out[35]: array([ 5, 15])

I found one way, which is:

    In [61]: x[[1,3]][:,[1,3]]
Out[61]: 
array([[ 5,  7],
       [13, 15]])

First issue with this is that it is hardly readable, although I can live with that. If someone has a better solution, I’d certainly like to hear it.

Other thing is I read on a forum that indexing arrays with arrays forces NumPy to make a copy of the desired array, thus when treating with large arrays this could become a problem. Why is that so / how does this mechanism work?


回答 0

如Sven所述,x[[[0],[2]],[1,3]]将返回与1和3列匹配的0和2行,同时x[[0,2],[1,3]]将在数组中返回值x [0,1]和x [2,3]。

有一个有用的函数可以帮助我完成第一个示例numpy.ix_。您可以使用进行与我的第一个示例相同的操作x[numpy.ix_([0,2],[1,3])]。这样可以避免您必须输入所有这些多余的括号。

As Sven mentioned, x[[[0],[2]],[1,3]] will give back the 0 and 2 rows that match with the 1 and 3 columns while x[[0,2],[1,3]] will return the values x[0,1] and x[2,3] in an array.

There is a helpful function for doing the first example I gave, numpy.ix_. You can do the same thing as my first example with x[numpy.ix_([0,2],[1,3])]. This can save you from having to enter in all of those extra brackets.


回答 1

为了回答这个问题,我们必须研究如何在Numpy中为多维数组建立索引。首先说您有x问题中的数组。分配给的缓冲区x将包含从0到15的16个升序整数。如果要访问一个元素,例如x[i,j]NumPy必须找出该元素相对于缓冲区起始位置的存储位置。这是通过有效计算i*x.shape[1]+j(并乘以一个int的大小以获得实际的内存偏移量)来完成的。

如果通过基本切片提取子y = x[0:2,0:2]数组,则结果对象将与共享基础缓冲区x。但是,如果您同意,会发生什么y[i,j]?NumPy无法用于i*y.shape[1]+j计算数组中的偏移量,因为所属的数据y在内存中不是连续的。

NumPy通过引入步幅来解决此问题。在计算要访问的内存偏移量时x[i,j],实际计算的是i*x.strides[0]+j*x.strides[1](并且这已经包括int大小的因数):

x.strides
(16, 4)

y像上面那样提取时,NumPy不会创建新的缓冲区,但是创建一个引用相同缓冲区的新数组对象(否则y将等于x。)然后,新数组对象将具有不同的形状,x并且可能以不同的开头偏移到缓冲区中,但将与x(至少在这种情况下)共享跨步:

y.shape
(2,2)
y.strides
(16, 4)

这样,计算的内存偏移量y[i,j]将产生正确的结果。

但是NumPy应该做什么z=x[[1,3]]呢?如果原始缓冲区用于,则跨步机制将不允许正确的索引编制z。从理论上讲 NumPy 可以添加比跨步更复杂的机制,但是这会使元素访问相对昂贵,从而在某种程度上违背了数组的整体思想。此外,视图不再是真正的轻量级对象。

关于索引的NumPy文档对此进行了详细介绍

哦,几乎忘了您的实际问题:这是如何使具有多个列表的索引按预期工作:

x[[[1],[3]],[1,3]]

这是因为索引数组以相同的形状广播。当然,对于此特定示例,您也可以使用基本切片:

x[1::2, 1::2]

To answer this question, we have to look at how indexing a multidimensional array works in Numpy. Let’s first say you have the array x from your question. The buffer assigned to x will contain 16 ascending integers from 0 to 15. If you access one element, say x[i,j], NumPy has to figure out the memory location of this element relative to the beginning of the buffer. This is done by calculating in effect i*x.shape[1]+j (and multiplying with the size of an int to get an actual memory offset).

If you extract a subarray by basic slicing like y = x[0:2,0:2], the resulting object will share the underlying buffer with x. But what happens if you acces y[i,j]? NumPy can’t use i*y.shape[1]+j to calculate the offset into the array, because the data belonging to y is not consecutive in memory.

NumPy solves this problem by introducing strides. When calculating the memory offset for accessing x[i,j], what is actually calculated is i*x.strides[0]+j*x.strides[1] (and this already includes the factor for the size of an int):

x.strides
(16, 4)

When y is extracted like above, NumPy does not create a new buffer, but it does create a new array object referencing the same buffer (otherwise y would just be equal to x.) The new array object will have a different shape then x and maybe a different starting offset into the buffer, but will share the strides with x (in this case at least):

y.shape
(2,2)
y.strides
(16, 4)

This way, computing the memory offset for y[i,j] will yield the correct result.

But what should NumPy do for something like z=x[[1,3]]? The strides mechanism won’t allow correct indexing if the original buffer is used for z. NumPy theoretically could add some more sophisticated mechanism than the strides, but this would make element access relatively expensive, somehow defying the whole idea of an array. In addition, a view wouldn’t be a really lightweight object anymore.

This is covered in depth in the NumPy documentation on indexing.

Oh, and nearly forgot about your actual question: Here is how to make the indexing with multiple lists work as expected:

x[[[1],[3]],[1,3]]

This is because the index arrays are broadcasted to a common shape. Of course, for this particular example, you can also make do with basic slicing:

x[1::2, 1::2]

回答 2

我认为这x[[1,3]][:,[1,3]]很难理解。如果您想更加清楚自己的意图,可以执行以下操作:

a[[1,3],:][:,[1,3]]

我不是切片专家,但是通常情况下,如果尝试切片为数组并且值是连续的,则会返回一个视图,其中步幅值已更改。

例如,在输入33和34中,尽管得到2×2数组,步幅为4。因此,当索引下一行时,指针将移动到内存中的正确位置。

显然,这种机制不能很好地用于索引数组的情况。因此,numpy将必须进行复制。毕竟,许多其他矩阵数学函数依赖于大小,步幅和连续的内存分配。

I don’t think that x[[1,3]][:,[1,3]] is hardly readable. If you want to be more clear on your intent, you can do:

a[[1,3],:][:,[1,3]]

I am not an expert in slicing but typically, if you try to slice into an array and the values are continuous, you get back a view where the stride value is changed.

e.g. In your inputs 33 and 34, although you get a 2×2 array, the stride is 4. Thus, when you index the next row, the pointer moves to the correct position in memory.

Clearly, this mechanism doesn’t carry well into the case of an array of indices. Hence, numpy will have to make the copy. After all, many other matrix math function relies on size, stride and continuous memory allocation.


回答 3

如果要跳过每隔一行和每隔一列,则可以使用基本切片:

In [49]: x=np.arange(16).reshape((4,4))
In [50]: x[1:4:2,1:4:2]
Out[50]: 
array([[ 5,  7],
       [13, 15]])

这将返回一个视图,而不是数组的副本。

In [51]: y=x[1:4:2,1:4:2]

In [52]: y[0,0]=100

In [53]: x   # <---- Notice x[1,1] has changed
Out[53]: 
array([[  0,   1,   2,   3],
       [  4, 100,   6,   7],
       [  8,   9,  10,  11],
       [ 12,  13,  14,  15]])

z=x[(1,3),:][:,(1,3)]使用高级索引并因此返回副本:

In [58]: x=np.arange(16).reshape((4,4))
In [59]: z=x[(1,3),:][:,(1,3)]

In [60]: z
Out[60]: 
array([[ 5,  7],
       [13, 15]])

In [61]: z[0,0]=0

请注意,它x是不变的:

In [62]: x
Out[62]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

如果要选择任意行和列,则不能使用基本切片。您必须使用高级索引,使用类似x[rows,:][:,columns],where rowscolumnsare sequence的方法。当然,这将为您提供原始阵列的副本,而不是视图。正如人们所期望的那样,因为numpy数组使用连续内存(具有恒定的步幅),并且将无法生成具有任意行和列的视图(因为这将需要非恒定的步幅)。

If you want to skip every other row and every other column, then you can do it with basic slicing:

In [49]: x=np.arange(16).reshape((4,4))
In [50]: x[1:4:2,1:4:2]
Out[50]: 
array([[ 5,  7],
       [13, 15]])

This returns a view, not a copy of your array.

In [51]: y=x[1:4:2,1:4:2]

In [52]: y[0,0]=100

In [53]: x   # <---- Notice x[1,1] has changed
Out[53]: 
array([[  0,   1,   2,   3],
       [  4, 100,   6,   7],
       [  8,   9,  10,  11],
       [ 12,  13,  14,  15]])

while z=x[(1,3),:][:,(1,3)] uses advanced indexing and thus returns a copy:

In [58]: x=np.arange(16).reshape((4,4))
In [59]: z=x[(1,3),:][:,(1,3)]

In [60]: z
Out[60]: 
array([[ 5,  7],
       [13, 15]])

In [61]: z[0,0]=0

Note that x is unchanged:

In [62]: x
Out[62]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

If you wish to select arbitrary rows and columns, then you can’t use basic slicing. You’ll have to use advanced indexing, using something like x[rows,:][:,columns], where rows and columns are sequences. This of course is going to give you a copy, not a view, of your original array. This is as one should expect, since a numpy array uses contiguous memory (with constant strides), and there would be no way to generate a view with arbitrary rows and columns (since that would require non-constant strides).


回答 4

使用numpy时,您可以为索引的每个部分传递一个切片-因此,x[0:2,0:2]上面的示例有效。

如果您只想平均跳过列或行,则可以传递包含三个成分(即开始,停止,步进)的切片。

同样,对于上面的示例:

>>> x[1:4:2, 1:4:2]
array([[ 5,  7],
       [13, 15]])

这基本上是:第一维中的切片,从索引1开始,在索引等于或大于4时停止,并在每次遍历中将2加到索引上。第二维相同。同样:这仅适用于恒定的步骤。

您必须在内部执行完全不同的语法- x[[1,3]][:,[1,3]]实际要做的是创建一个仅包含原始数组中第1行和第3行的新数组(与x[[1,3]]部件一起完成),然后重新切片-创建第三个数组-仅包含上一个数组的第1列和第3列。

With numpy, you can pass a slice for each component of the index – so, your x[0:2,0:2] example above works.

If you just want to evenly skip columns or rows, you can pass slices with three components (i.e. start, stop, step).

Again, for your example above:

>>> x[1:4:2, 1:4:2]
array([[ 5,  7],
       [13, 15]])

Which is basically: slice in the first dimension, with start at index 1, stop when index is equal or greater than 4, and add 2 to the index in each pass. The same for the second dimension. Again: this only works for constant steps.

The syntax you got to do something quite different internally – what x[[1,3]][:,[1,3]] actually does is create a new array including only rows 1 and 3 from the original array (done with the x[[1,3]] part), and then re-slice that – creating a third array – including only columns 1 and 3 of the previous array.


回答 5

我在这里有一个类似的问题:以最Python的方式在ndarray的sub-ndarray中编写。Python 2

遵循针对您的案例的上一篇解决方案后,解决方案如下所示:

columns_to_keep = [1,3] 
rows_to_keep = [1,3]

使用ix_:

x[np.ix_(rows_to_keep, columns_to_keep)] 

这是:

array([[ 5,  7],
       [13, 15]])

I have a similar question here: Writting in sub-ndarray of a ndarray in the most pythonian way. Python 2 .

Following the solution of previous post for your case the solution looks like:

columns_to_keep = [1,3] 
rows_to_keep = [1,3]

An using ix_:

x[np.ix_(rows_to_keep, columns_to_keep)] 

Which is:

array([[ 5,  7],
       [13, 15]])

回答 6

我不确定这有多有效,但是您可以使用range()在两个轴上切片

 x=np.arange(16).reshape((4,4))
 x[range(1,3), :][:,range(1,3)] 

I’m not sure how efficient this is but you can use range() to slice in both axis

 x=np.arange(16).reshape((4,4))
 x[range(1,3), :][:,range(1,3)] 

我怎样才能告诉PyCharm参数期望是什么类型?

问题:我怎样才能告诉PyCharm参数期望是什么类型?

当涉及到构造函数,赋值和方法调用时,PyCharm IDE非常擅长分析我的源代码并弄清楚每个变量应该是什么类型。我很喜欢它,因为它给了我很好的代码完成和参数信息,并且如果我尝试访问一个不存在的属性,它会给我警告。

但是当涉及到参数时,它一无所知。代码完成下拉列表无法显示任何内容,因为它们不知道参数的类型。代码分析无法查找警告。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

peasant = Person("Dennis", 37)
# PyCharm knows that the "peasant" variable is of type Person
peasant.dig_filth()   # shows warning -- Person doesn't have a dig_filth method

class King:
    def repress(self, peasant):
        # PyCharm has no idea what type the "peasant" parameter should be
        peasant.knock_over()   # no warning even though knock_over doesn't exist

King().repress(peasant)
# Even if I call the method once with a Person instance, PyCharm doesn't
# consider that to mean that the "peasant" parameter should always be a Person

这在一定程度上是有意义的。其他呼叫站点可以为该参数传递任何内容。但是,如果我的方法希望参数的类型为,则pygame.Surface我希望能够以某种方式向PyCharm指出,因此它可以Surface在其代码完成下拉列表中向我显示所有的属性,并在警告时突出显示警告我调用了错误的方法,依此类推。

有没有办法给PyCharm一个提示,然后说“ psst,该参数应该是X类型”?(或者,也许是本着动态语言的精神,“这个参数应该像X一样嘎嘎叫”?对此我可以接受。)


编辑:下面的CrazyCoder的答案就可以了。对于像我这样想要快速摘要的任何新手,这里是:

class King:
    def repress(self, peasant):
        """
        Exploit the workers by hanging on to outdated imperialist dogma which
        perpetuates the economic and social differences in our society.

        @type peasant: Person
        @param peasant: Person to repress.
        """
        peasant.knock_over()   # Shows a warning. And there was much rejoicing.

相关部分是@type peasant: Person文档字符串的行。

如果还转到“文件”>“设置”>“ Python集成工具”,并将“文档字符串格式”设置为“ Epytext”,则PyCharm的“视图”>“快速文档查找”将漂亮地打印参数信息,而不是仅按原样打印所有@ -lines。

When it comes to constructors, and assignments, and method calls, the PyCharm IDE is pretty good at analyzing my source code and figuring out what type each variable should be. I like it when it’s right, because it gives me good code-completion and parameter info, and it gives me warnings if I try to access an attribute that doesn’t exist.

But when it comes to parameters, it knows nothing. The code-completion dropdowns can’t show anything, because they don’t know what type the parameter will be. The code analysis can’t look for warnings.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

peasant = Person("Dennis", 37)
# PyCharm knows that the "peasant" variable is of type Person
peasant.dig_filth()   # shows warning -- Person doesn't have a dig_filth method

class King:
    def repress(self, peasant):
        # PyCharm has no idea what type the "peasant" parameter should be
        peasant.knock_over()   # no warning even though knock_over doesn't exist

King().repress(peasant)
# Even if I call the method once with a Person instance, PyCharm doesn't
# consider that to mean that the "peasant" parameter should always be a Person

This makes a certain amount of sense. Other call sites could pass anything for that parameter. But if my method expects a parameter to be of type, say, pygame.Surface, I’d like to be able to indicate that to PyCharm somehow, so it can show me all of Surface‘s attributes in its code-completion dropdown, and highlight warnings if I call the wrong method, and so on.

Is there a way I can give PyCharm a hint, and say “psst, this parameter is supposed to be of type X”? (Or perhaps, in the spirit of dynamic languages, “this parameter is supposed to quack like an X”? I’d be fine with that.)


EDIT: CrazyCoder’s answer, below, does the trick. For any newcomers like me who want the quick summary, here it is:

class King:
    def repress(self, peasant):
        """
        Exploit the workers by hanging on to outdated imperialist dogma which
        perpetuates the economic and social differences in our society.

        @type peasant: Person
        @param peasant: Person to repress.
        """
        peasant.knock_over()   # Shows a warning. And there was much rejoicing.

The relevant part is the @type peasant: Person line of the docstring.

If you also go to File > Settings > Python Integrated Tools and set “Docstring format” to “Epytext”, then PyCharm’s View > Quick Documentation Lookup will pretty-print the parameter information instead of just printing all the @-lines as-is.


回答 0

是的,您可以对方法及其参数使用特殊的文档格式,以便PyCharm可以知道类型。最新的PyCharm版本支持大多数常见的doc格式

例如,PyCharm从@param样式注释中提取类型。

另请参见reStructuredTextdocstring约定(PEP 257)。

另一个选择是Python 3注释。

参阅PyCharm文档部分以获取更多详细信息和示例。

Yes, you can use special documentation format for methods and their parameters so that PyCharm can know the type. Recent PyCharm version supports most common doc formats.

For example, PyCharm extracts types from @param style comments.

See also reStructuredText and docstring conventions (PEP 257).

Another option is Python 3 annotations.

Please refer to the PyCharm documentation section for more details and samples.


回答 1

如果您使用的是Python 3.0或更高版本,则还可以在函数和参数上使用注释。PyCharm会将这些解释为参数或返回值应具有的类型:

class King:
    def repress(self, peasant: Person) -> bool:
        peasant.knock_over() # Shows a warning. And there was much rejoicing.

        return peasant.badly_hurt() # Lets say, its not known from here that this method will always return a bool

有时,这对于不需要文档字符串的非公共方法很有用。另外一个好处是,这些注释可以通过代码访问:

>>> King.repress.__annotations__
{'peasant': <class '__main__.Person'>, 'return': <class 'bool'>}

更新:从PEP 484(已为Python 3.5接受)开始,使用注释指定参数和返回类型也是官方约定。

If you are using Python 3.0 or later, you can also use annotations on functions and parameters. PyCharm will interpret these as the type the arguments or return values are expected to have:

class King:
    def repress(self, peasant: Person) -> bool:
        peasant.knock_over() # Shows a warning. And there was much rejoicing.

        return peasant.badly_hurt() # Lets say, its not known from here that this method will always return a bool

Sometimes this is useful for non-public methods, that do not need a docstring. As an added benefit, those annotations can be accessed by code:

>>> King.repress.__annotations__
{'peasant': <class '__main__.Person'>, 'return': <class 'bool'>}

Update: As of PEP 484, which has been accepted for Python 3.5, it is also the official convention to specify argument and return types using annotations.


回答 2

PyCharm从@type pydoc字符串中提取类型。在此处此处查看PyCharm文档,以及Epydoc文档。它位于PyCharm的“旧版”部分,也许缺少某些功能。

class King:
    def repress(self, peasant):
        """
        Exploit the workers by hanging on to outdated imperialist dogma which
        perpetuates the economic and social differences in our society.

        @type peasant: Person
        @param peasant: Person to repress.
        """
        peasant.knock_over()   # Shows a warning. And there was much rejoicing.

相关部分是@type peasant: Person文档字符串的行。

我的目的不是要从CrazyCoder或原始提问者那里窃取分数,而应尽一切可能给予分数。我只是以为简单的答案应该在“答案”栏中。

PyCharm extracts types from a @type pydoc string. See PyCharm docs here and here, and Epydoc docs. It’s in the ‘legacy’ section of PyCharm, perhaps it lacks some functionality.

class King:
    def repress(self, peasant):
        """
        Exploit the workers by hanging on to outdated imperialist dogma which
        perpetuates the economic and social differences in our society.

        @type peasant: Person
        @param peasant: Person to repress.
        """
        peasant.knock_over()   # Shows a warning. And there was much rejoicing.

The relevant part is the @type peasant: Person line of the docstring.

My intention is not to steal points from CrazyCoder or the original questioner, by all means give them their points. I just thought the simple answer should be in an ‘answer’ slot.


回答 3

我正在使用PyCharm Professional 2016.1编写py2.6-2.7代码,发现使用reStructuredText可以以更简洁的方式表达类型:

class Replicant(object):
    pass


class Hunter(object):
    def retire(self, replicant):
        """ Retire the rogue or non-functional replicant.
        :param Replicant replicant: the replicant to retire.
        """
        replicant.knock_over()  # Shows a warning.

参见:https : //www.jetbrains.com/help/pycharm/2016.1/type-hinting-in-pycharm.html#legacy

I’m using PyCharm Professional 2016.1 writing py2.6-2.7 code, and I found that using reStructuredText I can express types in a more succint way:

class Replicant(object):
    pass


class Hunter(object):
    def retire(self, replicant):
        """ Retire the rogue or non-functional replicant.
        :param Replicant replicant: the replicant to retire.
        """
        replicant.knock_over()  # Shows a warning.

See: https://www.jetbrains.com/help/pycharm/2016.1/type-hinting-in-pycharm.html#legacy


回答 4

您还可以断言一个类型,Pycharm会推断出它:

def my_function(an_int):
    assert isinstance(an_int, int)
    # Pycharm now knows that an_int is of type int
    pass

You can also assert for a type and Pycharm will infer it:

def my_function(an_int):
    assert isinstance(an_int, int)
    # Pycharm now knows that an_int is of type int
    pass