Python matplotlib中是否有任何方法可以获取艺术家的值,这些值是实例化此类艺术家所必需的?

Python matplotlib中是否有任何方法可以获取艺术家的值,这些值是实例化此类艺术家所必需的?,python,matplotlib,Python,Matplotlib,我的意思是:给定一个泛型matplotlib的实例,是否有一种泛型方法来获取该艺术家的位置参数值,从而实例化相同类型的另一个艺术家?我知道有-方法,但根据我的经验,它只返回关键字参数,而不返回位置参数 我已经了解了这个模块,并且我设法使用它至少获得了给定艺术家类型的位置参数的名称,但是我没有得到更多,因为艺术家的相应属性通常有不同的名称 import inspect from matplotlib.lines import Line2D artist = Line2D([0, 1], [2,

我的意思是:给定一个泛型matplotlib的实例,是否有一种泛型方法来获取该艺术家的位置参数值,从而实例化相同类型的另一个艺术家?我知道有-方法,但根据我的经验,它只返回关键字参数,而不返回位置参数

我已经了解了这个模块,并且我设法使用它至少获得了给定艺术家类型的位置参数的名称,但是我没有得到更多,因为艺术家的相应属性通常有不同的名称

import inspect
from matplotlib.lines import Line2D


artist = Line2D([0, 1], [2, 3])
sig = inspect.signature(type(artist))
args = {}
for param in sig.parameters.values():
    if (
        param.default == param.empty
        and param.kind != inspect.Parameter.VAR_KEYWORD
    ):
        args[param.name] = getattr(artist, param.name)

new_artist = type(artist)(**args)

好吧,经过无数个小时的研究,陷入各种各样的兔子洞,在相同的想法中盘旋(其中一些被记录在案),我现在终于放弃了。我在这里发布的最后一种方法实际上是手动硬编码函数,根据艺术家的类型检索相关信息。但即使这种方法最终也失败了,因为:

  • 使用本应将属性从一个艺术家复制到下一个艺术家的,例如
    artist.update_from()
    不幸的是:

    • 也可以复制不兼容的属性,也就是说,如果在将新艺术家添加到其他轴后运行该方法,则会引发错误
    • 或者看起来根本不复制任何属性,例如
      axesimage
    这意味着您必须想出自己的方法来单独复制这些信息。这同样非常麻烦,但更重要的是:

  • 对于某些艺术家类型,根本不可能从实例中检索所有必要的
    args
    kwargs
    。这是一个例子实际上,用于初始化该类的KWARG和arg中只有两个不能从艺术家实例本身以任何方式再次访问。这真是令人难以置信的沮丧
  • 无论如何,这里是我硬编码复制方法的尝试,也许它们对其他人有用

    重复.py

    from matplotlib.patches import Ellipse, Rectangle, FancyArrow
    from matplotlib.text import Annotation, Text
    from matplotlib.lines import Line2D
    from matplotlib.image import AxesImage
    from matplotlib.artist import Artist
    from matplotlib.axes import Axes
    from matplotlib_scalebar.scalebar import ScaleBar
    from typing import Callable
    
    
    def _get_ellipse_args(ellipse: Ellipse) -> list:
        return [ellipse.center, ellipse.width, ellipse.height]
    
    
    def _get_rectangle_args(rectangle: Rectangle) -> list:
        return [rectangle.get_xy(), rectangle.get_width(), rectangle.get_height()]
    
    
    def _get_fancy_arroy_args(arrow: FancyArrow) -> list:
        return [*arrow.xy, arrow.dx, arrow.dy]
    
    
    def _get_scalebar_args(scalebar: ScaleBar) -> list:
        return [scalebar.dx]
    
    
    def _get_line2d_args(line: Line2D) -> list:
        return line.get_data()
    
    
    def _get_text_args(text: Text) -> list:
        return []
    
    
    def _get_annotation_args(text: Text) -> list:
        return [text.get_text(), text.xy]
    
    
    class Duplicator:
        _arg_fetchers = {
            Line2D: _get_line2d_args,
            # Ellipse: _get_ellipse_args,
            Rectangle: _get_rectangle_args,
            Text: _get_text_args,
            Annotation: _get_annotation_args,
            ScaleBar: _get_scalebar_args,
            AxesImage: lambda: None,
        }
    
        def args(self, artist):
            return self._arg_fetchers[type(artist)](artist)
    
        @classmethod
        def duplicate(
                cls, artist: Artist, other_ax: Axes,
                duplication_method: Callable = None
        ) -> Artist:
            if duplication_method is not None:
                cls._arg_fetchers[type(artist)] = duplication_method
            if type(artist) in cls._arg_fetchers:
                if isinstance(artist, AxesImage):
                    duplicate = other_ax.imshow(artist.get_array())
                    # duplicate.update_from(artist) has no effect on AxesImage
                    # instances for some reason.
                    # duplicate.update(artist.properties()) causes an
                    # AttributeError for some other reason.
                    # Thus it seems kwargs need to be set individually for
                    # AxesImages.
                    duplicate.set_cmap(artist.get_cmap())
                    duplicate.set_clim(artist.get_clim())
                else:
                    duplicate = type(artist)(*cls.args(cls, artist))
                    # this unfortunately copies properties that should not be
                    # copied, resulting in the artist being absent in the new axes
                    # duplicate.update_from(artist)
                    other_ax.add_artist(duplicate)
    
                return duplicate
            else:
                raise TypeError(
                    'There is no duplication method for this type of artist',
                    type(artist)
                )
    
        @classmethod
        def can_duplicate(cls, artist: Artist) -> bool:
            return type(artist) in cls._arg_fetchers
    
    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.patches import Ellipse
    from matplotlib.text import Annotation
    from matplotlib.lines import Line2D
    
    from duplicate import Duplicator, _get_ellipse_args
    
    
    fig, (ax1, ax2, ax3) = plt.subplots(1, 3)
    
    # determine artists that were there before we manually added some
    default_artists1 = set(ax1.get_children())
    default_artists2 = set(ax2.get_children())
    
    # add several artists to ax1
    ax1.add_line(Line2D([0, 1], [2, 3], lw=4, color='red'))
    ax1.add_patch(Ellipse((1, 1), 1, 1))
    ax1.imshow(np.random.uniform(0, 1, (10, 10)))
    ax2.add_patch(Ellipse((3, 5), 2, 4, fc='purple'))
    ax2.add_artist(Annotation('text', (1, 1), fontsize=20))
    
    # set axes limits, optional, but usually necessary
    for ax in [ax1, ax2]:
        ax.relim()
        ax.autoscale_view()
    
    ax2.axis('square')
    for ax in [ax2, ax3]:
        ax.set_xlim(ax1.get_xlim())
        ax.set_ylim(ax1.get_ylim())
    
    # determine artists that were added manually
    new_artists1 = set(ax1.get_children()) - default_artists1
    new_artists2 = set(ax2.get_children()) - default_artists2
    new_artists = new_artists1 | new_artists2
    
    # declare our own arg fetchers for artists types that may not be
    # covered by the Duplicator class
    arg_fetchers = {Ellipse: _get_ellipse_args}
    
    # duplicate artists to ax3
    for artist in new_artists:
        if Duplicator.can_duplicate(artist):
            Duplicator.duplicate(artist, ax3)
        else:
            Duplicator.duplicate(artist, ax3, arg_fetchers[type(artist)])
    
    fig.show()
    
    重复测试.py

    from matplotlib.patches import Ellipse, Rectangle, FancyArrow
    from matplotlib.text import Annotation, Text
    from matplotlib.lines import Line2D
    from matplotlib.image import AxesImage
    from matplotlib.artist import Artist
    from matplotlib.axes import Axes
    from matplotlib_scalebar.scalebar import ScaleBar
    from typing import Callable
    
    
    def _get_ellipse_args(ellipse: Ellipse) -> list:
        return [ellipse.center, ellipse.width, ellipse.height]
    
    
    def _get_rectangle_args(rectangle: Rectangle) -> list:
        return [rectangle.get_xy(), rectangle.get_width(), rectangle.get_height()]
    
    
    def _get_fancy_arroy_args(arrow: FancyArrow) -> list:
        return [*arrow.xy, arrow.dx, arrow.dy]
    
    
    def _get_scalebar_args(scalebar: ScaleBar) -> list:
        return [scalebar.dx]
    
    
    def _get_line2d_args(line: Line2D) -> list:
        return line.get_data()
    
    
    def _get_text_args(text: Text) -> list:
        return []
    
    
    def _get_annotation_args(text: Text) -> list:
        return [text.get_text(), text.xy]
    
    
    class Duplicator:
        _arg_fetchers = {
            Line2D: _get_line2d_args,
            # Ellipse: _get_ellipse_args,
            Rectangle: _get_rectangle_args,
            Text: _get_text_args,
            Annotation: _get_annotation_args,
            ScaleBar: _get_scalebar_args,
            AxesImage: lambda: None,
        }
    
        def args(self, artist):
            return self._arg_fetchers[type(artist)](artist)
    
        @classmethod
        def duplicate(
                cls, artist: Artist, other_ax: Axes,
                duplication_method: Callable = None
        ) -> Artist:
            if duplication_method is not None:
                cls._arg_fetchers[type(artist)] = duplication_method
            if type(artist) in cls._arg_fetchers:
                if isinstance(artist, AxesImage):
                    duplicate = other_ax.imshow(artist.get_array())
                    # duplicate.update_from(artist) has no effect on AxesImage
                    # instances for some reason.
                    # duplicate.update(artist.properties()) causes an
                    # AttributeError for some other reason.
                    # Thus it seems kwargs need to be set individually for
                    # AxesImages.
                    duplicate.set_cmap(artist.get_cmap())
                    duplicate.set_clim(artist.get_clim())
                else:
                    duplicate = type(artist)(*cls.args(cls, artist))
                    # this unfortunately copies properties that should not be
                    # copied, resulting in the artist being absent in the new axes
                    # duplicate.update_from(artist)
                    other_ax.add_artist(duplicate)
    
                return duplicate
            else:
                raise TypeError(
                    'There is no duplication method for this type of artist',
                    type(artist)
                )
    
        @classmethod
        def can_duplicate(cls, artist: Artist) -> bool:
            return type(artist) in cls._arg_fetchers
    
    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.patches import Ellipse
    from matplotlib.text import Annotation
    from matplotlib.lines import Line2D
    
    from duplicate import Duplicator, _get_ellipse_args
    
    
    fig, (ax1, ax2, ax3) = plt.subplots(1, 3)
    
    # determine artists that were there before we manually added some
    default_artists1 = set(ax1.get_children())
    default_artists2 = set(ax2.get_children())
    
    # add several artists to ax1
    ax1.add_line(Line2D([0, 1], [2, 3], lw=4, color='red'))
    ax1.add_patch(Ellipse((1, 1), 1, 1))
    ax1.imshow(np.random.uniform(0, 1, (10, 10)))
    ax2.add_patch(Ellipse((3, 5), 2, 4, fc='purple'))
    ax2.add_artist(Annotation('text', (1, 1), fontsize=20))
    
    # set axes limits, optional, but usually necessary
    for ax in [ax1, ax2]:
        ax.relim()
        ax.autoscale_view()
    
    ax2.axis('square')
    for ax in [ax2, ax3]:
        ax.set_xlim(ax1.get_xlim())
        ax.set_ylim(ax1.get_ylim())
    
    # determine artists that were added manually
    new_artists1 = set(ax1.get_children()) - default_artists1
    new_artists2 = set(ax2.get_children()) - default_artists2
    new_artists = new_artists1 | new_artists2
    
    # declare our own arg fetchers for artists types that may not be
    # covered by the Duplicator class
    arg_fetchers = {Ellipse: _get_ellipse_args}
    
    # duplicate artists to ax3
    for artist in new_artists:
        if Duplicator.can_duplicate(artist):
            Duplicator.duplicate(artist, ax3)
        else:
            Duplicator.duplicate(artist, ax3, arg_fetchers[type(artist)])
    
    fig.show()
    

    我真的不明白为什么matplotlib不在不同的图形/轴上重复使用同一个艺术家(这可能有一个很好的技术原因),但他们同时制作,如果不是至少移动或复制艺术家的话


    Matplotlib诸神,拜托,尽管这很好,但还是引入了一种复制艺术家的标准方法。这看起来有点像XY问题。你只是想复制对象吗?如果是这样,我建议使用标准库的
    copy
    模块。你是对的,这基本上是一个xy问题。你们猜对了,我真的想抄写给艺术家。但因为我读到不建议将艺术家从一个人物复制到另一个人物,所以我想从头开始创建一个具有相同属性的新艺术家。我的意思是,如果我使用
    copy
    ,matplotlib不会抱怨艺术家是在另一个人物中创作的,不应该被移动吗?嗯,我从未尝试过,但我同意复制是有问题的。使用
    Artist.set\u figure
    方法可以“移动”副本,但这可能只适用于尚未在图形上的副本。您是否完全按照此处所示创建了艺术家?如中所示,您是通过构造而不是通过类似于
    plt.plot()
    ?这看起来很相关:。对于
    properties()
    中的每个键,您基本上必须
    setattr(新艺术家,姓名,getattr(旧艺术家,姓名))
    。谢谢您的建议!不过,这可以简单得多,因为有一些方便的函数可以处理这一点,例如
    artist.update\u from()
    ,如文档所示。不过,对于大多数艺术家来说,这主要只处理
    kwargs
    ,而不处理
    args
    。出于某些原因,没有方便的方法返回艺术家的
    参数。如果您想确保mpl开发者看到/解决此问题,请打开问题/拉取请求或将其重新发布到Distance.matplotlib.org。谢谢您的建议,我将尝试。