【问题标题】:Passing object method as the object argument value将对象方法作为对象参数值传递
【发布时间】:2020-06-05 01:01:24
【问题描述】:

我创建了一个BetterMarker 类,它继承自folium.Marker,这是一个地理点对象,可以添加到生成Leaflet HTML 地图的folium.Map 对象。

import folium, datetime

class BetterMarker(folium.Marker):

    def __init__(self, latitude, longitude, **kwargs): 
        super().__init__([latitude, longitude], **kwargs)
        self.latitude = latitude

    def north_or_south(self):
        if self.latitude > 0:
            return "North"
        else:
            return "South"

我现在可以初始化BetterMarker 并将其实例添加到folium.Map 对象:

mymap = folium.Map() # Create Map object
bm = BetterMarker(30, 40, popup = "Hi there")
bm.add_to(mymap) # Add BetterMarker object to Map
mymap.save("mymap.html") # Save the map with the marker into HTML

上面的代码可以很好地生成以下地图:

但是,我不想在bm = BetterMarker(30, 40, popup = "Hi there") 中使用字符串Hi there,而是使用bm.north_or_south()

bm = BetterMarker(30, 40, popup = bm.north_or_south())

当然,以上行不通。下面的代码可以工作,但这意味着初始化类两次:

bm = BetterMarker(30, 40)
bm = BetterMarker(30, 40, popup = bm.north_or_south())

这样做的明智方法是什么?我做错了什么?

【问题讨论】:

  • 最好在 __init__() 函数中进行这些更改,而不是在对象初始化期间。 if not popup: self.popup = ...
  • @Felipe 不确定您要对 if not popup 做什么。为什么我需要检查 popup 是否为 None?
  • 因为您没有在此处传递它bm = BetterMarker(30, 40),但随后尝试通过在此处传递它bm = BetterMarker(30, 40, popup = bm.north_or_south()) 来重新初始化对象。所以相反,如果在BetterMaker 内部有if not popup:,那么你可以跳过两次初始化类的过程,如果没有传递给构造函数,则始终将popup 设置为函数调用north_or_south。跨度>
  • @Felipe 即使我在__init__ 中添加了if not popup,同样bm = BetterMarker(30, 40, popup = bm.north_or_south()) 将返回NameError: name 'bm' is not defined
  • 当然它会给你那个错误——想法是摆脱双重对象初始化。我在下面为您添加了一个示例。

标签: python inheritance methods folium


【解决方案1】:

根据评论更新

我的第一次尝试并没有完全理解要求。我认为其他一些答案是正确的。这是一种可行的方法:

class BetterMarker(folium.Marker):

    def __init__(self, latitude, longitude, **kwargs):
        if kwargs['popup_method']:
            kwargs['popup'] = kwargs['popup_method'](latitude, longitude, **kwargs)
            del kwargs['popup_method']
        super().__init__([latitude, longitude], **kwargs)

    @staticmethod
    def quadrant(latitude, longitude, **kwargs):
        if latitude > 0 and longitude > 0:
            return "North East"
        elif latitude > 0 and longitude <= 0:
            return "North West"
        elif latitude <= 0 and longitude > 0:
            return "South East"
        else:
            return "South West"

至于调用,重要的是只传递方法,不要通过括号来调用它:

bm = BetterMarker(-30, 40, popup_method=BetterMarker.quadrant)

请注意,我创建了一个名为 popup_method 的新关键字参数,因为 popup 表示文本。此外,我在弹出方法的签名中添加了经度和 kwargs,因此这些方法可以访问调用者传递的所有内容。

【讨论】:

  • BetterMarker 类除了north_or_south 之外还有其他方法,用户有时可能希望将这些方法调用分配给弹出窗口:bm = BetterMarker(30, 40, popup = bm.north_or_south()) bm = BetterMarker(30, 40, popup = bm.cold_or_hot()) 等。因此,在__init__ 中硬编码north_or_south 是行不通的.
  • 好的,更新后更好地了解您要做什么
【解决方案2】:

与有问题的 cmets 有关。像这样:

class Foo:
    def __init__(self, *args, **kwargs):
        self.alert = kwargs['alert']

    def print_alert(self):
        print(self.alert())

class Bar(Foo):
    def __init__(self, *args, **kwargs):
        if "alert" not in kwargs:
            kwargs['alert'] = self.custom_alert() # Notice here.

        super().__init__(*args, **kwargs)

    def custom_alert(self):
        return "Custom Alert"

Bar().print_alert() # >>> Custom Alert

编辑

根据下面的评论,您可以简单地将方法定义为@staticmethod

class Foo:
    def __init__(self, *args, **kwargs):
        self.alert = kwargs["alert"]

    def print_alert(self):
        print(self.alert()) # Notice we DO call the variable.


class Bar(Foo):
    def __init__(self, *args, **kwargs):
        if "alert" not in kwargs:
            kwargs["alert"] = self.default_alert

        super().__init__(*args, **kwargs)

    @staticmethod
    def default_alert():
        return "Custom Alert!"

    @staticmethod
    def random_alert():
        return "Random Alert!"


a = Bar()
a.print_alert()

b = Bar(alert=Bar.random_alert)  # Notice we do not CALL the method.
b.print_alert()

输出:

default
rand

【讨论】:

  • 我无法通过分配north_or_south/custom_alert 方法调用来硬编码弹出/警报。在实例化子类之后,用户可以分配给弹出/警报的方法可能不止一种。
  • 明白了。最初的问题没有完全定义,所以我做了一些假设。查看上面的编辑——您可以使用@staticmethods 来执行此操作。
【解决方案3】:

您可以尝试将 north_or_south 设为静态方法

@staticmethod
def north_or_south(latitude):
    if latitude > 0:
        return "North"
    else:
        return "South"

这使得它可以从一个对象或一个对象类运行,但函数本身不知道它正在运行的对象或类,所以你不能再拥有

self.north_or_south()

因为函数对对象一无所知。相反,我们将纬度作为参数传递

self.north_or_south(self.latitude)

瞧,它有效。代码有点长,但函数不再需要从哪里运行的对象。因此,在您的问题中,您只需

bm = BetterMarker(30, 40, popup = BetterMarker.north_or_south(40))

【讨论】:

    【解决方案4】:

    [编辑] 也许只是将弹出生成函数作为关键字参数传递?

    import folium, datetime
    
    class CustomPopup:
           @staticmethod
           def north_or_south(latitude, longtitude, **kwargs):
            if latitude > 0:
                return "North"
            else:
                return "South" 
    
            ...other methods
    
    class BetterMarker(folium.Marker):
    
        def __init__(self, latitude, longitude, **kwargs): 
            self.latitude = latitude
            if 'msg' in kwargs:
                 kwargs['popup'] = kwargs['msg'](latitude, longtitude, **kwargs)
            super().__init__([latitude, longitude], **kwargs)
    
    
    mymap = folium.Map() # Create Map object
    bm = BetterMarker(30, 40, msg=CustomPopup.north_or_south)
    bm.add_to(mymap) # Add BetterMarker object to Map
    mymap.save("mymap.html")
    

    【讨论】:

    • 感谢您的回答,但这不是解决方案,因为我在上面添加到 Felipe 答案的评论中解释了原因。
    • 如何将弹出方法移动到其他类并将其作为参数传递给标记类?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-29
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    • 2013-05-14
    • 1970-01-01
    相关资源
    最近更新 更多