python扩展方法

时间:2011-08-21 15:14:37

标签: python extension-methods

好的,在c#中我们有类似的东西:

public static string Destroy(this string s) { 
    return "";
}

所以基本上,当你有一个字符串时,你可以这样做:

str = "This is my string to be destroyed";
newstr = str.Destroy()
# instead of 
newstr = Destroy(str)

现在这很酷,因为在我看来它更具可读性。 python有类似的东西吗?我的意思是代替这样写:

x = SomeClass()
div = x.getMyDiv()
span = x.FirstChild(x.FirstChild(div)) # so instead of this I'd like to write:
span = div.FirstChild().FirstChild() # which is more readable to me

有什么建议吗?

8 个答案:

答案 0 :(得分:18)

您可以直接修改该类,有时也称为猴子修补。

def MyMethod(self):
      return self + self

MyClass.MyMethod = MyMethod
del(MyMethod)#clean up namespace

我不是100%确定你可以在像str这样的特殊类上执行此操作,但是对于用户定义的类来说这很好。

<强>更新

你在评论中证实我怀疑这对于像str这样的内置是不可能的。在这种情况下,我认为这类的C#扩展方法没有类似的东西。

最后,在C#和Python中,这些方法的便利性带来了相关的风险。使用这些技术可以使代码更难以理解和维护。

答案 1 :(得分:4)

我会在这里使用Adapter模式。因此,假设我们有一个Person类,并且在一个特定的位置上我们想添加一些与健康相关的方法。

from dataclasses import dataclass


@dataclass
class Person:
    name: str
    height: float  # in meters
    mass: float  # in kg


class PersonMedicalAdapter:
    person: Person

    def __init__(self, person: Person):
        self.person = person

    def __getattr__(self, item):
        return getattr(self.person, item)

    def get_body_mass_index(self) -> float:
        return self.person.mass / self.person.height ** 2


if __name__ == '__main__':
    person = Person('John', height=1.7, mass=76)
    person_adapter = PersonMedicalAdapter(person)

    print(person_adapter.name)  # Call to Person object field
    print(person_adapter.get_body_mass_index())  # Call to wrapper object method

我认为它是一种易于阅读但灵活且具有Python风格的解决方案。

答案 2 :(得分:3)

您可以在forbidden fruit

的帮助下通过猴子修补来更改内置类

但安装禁果需要 C编译器不受限制的环境,因此它可能无法运行或需要努力在Google App Engine,Heroku等上运行< / p>

我通过此库更改了Python 2.7中unicode类的行为,以解决土耳其语i,I大写/小写问题。

# -*- coding: utf8 -*-
# Redesigned by @guneysus

import __builtin__
from forbiddenfruit import curse

lcase_table = tuple(u'abcçdefgğhıijklmnoöprsştuüvyz')
ucase_table = tuple(u'ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZ')


def upper(data):
    data = data.replace('i',u'İ')
    data = data.replace(u'ı',u'I')
    result = ''
    for char in data:
        try:
            char_index = lcase_table.index(char)
            ucase_char = ucase_table[char_index]
        except:
            ucase_char = char
        result += ucase_char
    return result

curse(__builtin__.unicode, 'upper', upper)
class unicode_tr(unicode):
    """For Backward compatibility"""
    def __init__(self, arg):
        super(unicode_tr, self).__init__(*args, **kwargs)

if __name__ == '__main__':
    print u'istanbul'.upper()

答案 3 :(得分:1)

您可以通过以下context manager很好地实现此目标,该方法将方法添加到上下文块内的类或对象中,然后将其删除:

  newJsonx = dict()
  for item in data["resultsPage"]["results"]["calendarEntry"]:
      name = item["event"]["performance"][0]["displayName"]
      page_titles = [name]
      url = (
          'https://en.wikipedia.org/w/api.php'
          '?action=query'
          '&prop=info'
          '&inprop=subjectid'
          '&titles=' + '|'.join(page_titles) +
          '&format=json')
      json_response = requests.get(url).json()

      title_to_page_id  = {page_id for page_id, page_info in json_response['query']['pages'].items()}
      print(title_to_page_id)

      wikipedia_url = f'https://fr.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro&explaintext&redirects=1&titles={name}'
      r = requests.get(wikipedia_url)
      wikipedia_json = r.json()
      intro = wikipedia_json['query']['pages'][{title_to_page_id}]['extract']
      print(intro)

用法如下:

class extension_method:

    def __init__(self, obj, method):
        method_name = method.__name__
        setattr(obj, method_name, method)
        self.obj = obj
        self.method_name = method_name

    def __enter__(self):
        return self.obj

    def __exit__(self, type, value, traceback):
        # remove this if you want to keep the extension method after context exit
        delattr(self.obj, self.method_name)

答案 4 :(得分:0)

您可以按照以下方式执行您的要求:

def extension_method(self):
    #do stuff
class.extension_method = extension_method

答案 5 :(得分:0)

我认为 C# 中的扩展方法与普通方法调用几乎相同,在普通方法调用中,您传递实例然后传递参数和其他东西。

instance.method(*args, **kwargs)
method(instance, *args, **kwargs) # pretty much the same as above, I don't see much benefit of it getting implemented in python.

答案 6 :(得分:-1)

C#实现了扩展方法,因为它缺少第一类函数,Python有它们,并且它是&#34;包装&#34;的首选方法。 Python中不同类的通用功能。

有充分的理由相信Python永远不会有扩展方法,只需查看可用的内置插件:

len(o) calls o.__len__
iter(o) calls o.__iter__
next(o) calls o.next
format(o, s) calls o.__format__(s)

基本上,Python 喜欢功能。

答案 7 :(得分:-10)

一周后,我有一个最接近我所寻求的解决方案。解决方案包括使用getattr__getattr__。以下是感兴趣的人的一个例子。

class myClass:

    def __init__(self): pass

    def __getattr__(self, attr): 
        try:
            methodToCall = getattr(myClass, attr)
            return methodToCall(myClass(), self)
        except:
            pass

    def firstChild(self, node):
        # bla bla bla
    def lastChild(self, node):
        # bla bla bla 

x = myClass()
div = x.getMYDiv()
y = div.firstChild.lastChild 

我没有测试这个例子,我只是想让它知道谁可能会感兴趣。希望有所帮助。