资讯专栏INFORMATION COLUMN

在Python中使用静态,类或抽象方法的权威指南

cheukyin / 2827人阅读

摘要:在中,可以将和装饰器放在上面和的接口相反,你可以在抽象方法中实现代码并通过调用它在上面的例子中,继承来创建的每个都必须重写方法,但是可以使用来获取出处

Python中方法的工作方式

方法是存储在类属性中的函数,你可以用下面这种方式声明和访问一个函数

>>> class Pizza(object):
    ...     def __init__(self, size):
    ...         self.size = size
    ...     def get_size(self):
    ...         return self.size
    ...
    >>> Pizza.get_size
    

Python在这里说明了什么?Pizza类的属性get_size是unbound(未绑定的),这代表什么含义?我们调用一下就明白了:

 >>> Pizza.get_size()
    Traceback (most recent call last):
      File "", line 1, in 
    TypeError: unbound method get_size() must be called with Pizza instance as first argument (got nothing instead)

我们无法调用它(get_size),因为它没有绑定到Pizza的任何实例上,而且一个方法需要一个实例作为它的第一个参数(Python2中必须是类的实例,Python3没有这个强制要求),让我们试一下:

 >>> Pizza.get_size(Pizza(42))
    42

我们使用一个实例作为这个方法的第一个参数来调用它,没有出现任何问题。但是如果我说这不是一个方便的调用方法的方式,你将会同意我的观点。我们每次调用方法都要涉及(这里我理解是引用)类

来看Python打算为我们做些什么,就是它从Pizza类中绑定所有的方法到这个类的任何实例上。意思就是Pizza实例化后get_size这个属性是一个绑定方法,方法的第一个参数会是实例对象自己

>>> Pizza(42).get_size
    >
    >>> Pizza(42).get_size()
    42

意料之中,我们不需要为get_size传任何参数,自从被绑定后,它的self参数会自动设置为Pizza实例,下面是一个更明显的例子:

>>> m = Pizza(42).get_size
    >>> m()
    42

事实上是,你甚至不需要对Pizza引用,因为这个方法已经绑定到了这个对象

如果你想知道这个绑定方法绑定到了哪一个对象,这里有个快捷的方法:

 >>> m = Pizza(42).get_size
    >>> m.__self__
    <__main__.Pizza object at 0x7f3138827910>
    >>> # You could guess, look at this:
    ...
    >>> m == m.__self__.get_size
    True

明显可以看出,我们仍然保持对我们对象的引用,而且如果需要我们可以找到它

在Python3中,类中的函数不再被认为是未绑定的方法(应该是作为函数存在),如果需要,会作为一个函数绑定到对象上,所以原理是一样的(和Python2),只是模型被简化了

 >>> class Pizza(object):
    ...     def __init__(self, size):
    ...         self.size = size
    ...     def get_size(self):
    ...         return self.size
    ...
    >>> Pizza.get_size
    
静态方法

静态方法一种特殊方法,有时你想把代码归属到一个类中,但又不想和这个对象发生任何交互:

class Pizza(object):
        @staticmethod
        def mix_ingredients(x, y):
            return x + y

        def cook(self):
            return self.mix_ingredients(self.cheese, self.vegetables)

上面这个例子,mix_ingredients完全可以写成一个非静态方法,但是这样会将self作为第一个参数传入。在这个例子里,装饰器@staticmethod 会实现几个功能:

Python不会为Pizza的实例对象实例化一个绑定方法,绑定方法也是对象,会产生开销,静态方法可以避免这类情况

>>> Pizza().cook is Pizza().cook
        False
        >>> Pizza().mix_ingredients is Pizza.mix_ingredients
        True
        >>> Pizza().mix_ingredients is Pizza().mix_ingredients
        True

简化了代码的可读性,看到@staticmethod我们就会知道这个方法不会依赖这个对象的状态(一国两制,高度自治)
允许在子类中重写mix_ingredients方法。如果我们在顶级模型中定义了mix_ingredients函数,继承自Pizza的类除了重写,否则无法改变mix_ingredients的功能

类方法

什么是类方法,类方法是方法不会被绑定到一个对象,而是被绑定到一个类中

 >>> class Pizza(object):
    ...     radius = 42
    ...     @classmethod
    ...     def get_radius(cls):
    ...         return cls.radius
    ... 
    >>> 
    >>> Pizza.get_radius
    >
    >>> Pizza().get_radius
    >
    >>> Pizza.get_radius == Pizza().get_radius
    True
    >>> Pizza.get_radius()
    42

无论以何种方式访问这个方法,它都会被绑定到类中,它的第一个参数必须是类本身(记住类也是对象)

什么时候使用类方法,类方法在以下两种场合会有很好的效果:
1、工厂方法,为类创建实例,例如某种程度的预处理。如果我们使用@staticmethod代替,我们必须要在代码中硬编码Pizza(写死Pizza),这样从Pizza继承的类就不能使用了

 class Pizza(object):
            def __init__(self, ingredients):
                self.ingredients = ingredients

            @classmethod
            def from_fridge(cls, fridge):
                return cls(fridge.get_cheese() + fridge.get_vegetables())

2、使用静态方法调用静态方法,如果你需要将一个静态方法拆分为多个,可以使用类方法来避免硬编码类名。使用这种方法来声明我们的方法Pizza的名字永远不会被直接引用,而且继承和重写方法都很方便

class Pizza(object):
            def __init__(self, radius, height):
                self.radius = radius
                self.height = height

            @staticmethod
            def compute_area(radius):
                 return math.pi * (radius ** 2)

            @classmethod
            def compute_volume(cls, height, radius):
                 return height * cls.compute_area(radius)

            def get_volume(self):
                return self.compute_volume(self.height, self.radius)
抽象方法

抽象方法是定义在基类中的,可以是不提供任何功能代码的方法
在Python中简单的写抽象方法的方式是:

 class Pizza(object):
        def get_radius(self):
            raise NotImplementedError

继承自Pizza的类都必须要实现并重写get_redius,否则就会报错

这种方式的抽象方法有一个问题,如果你忘记实现了get_radius,只有在你调用这个方法的时候才会报错

  >>> Pizza()
    <__main__.Pizza object at 0x7fb747353d90>
    >>> Pizza().get_radius()
    Traceback (most recent call last):
      File "", line 1, in 
      File "", line 3, in get_radius
    NotImplementedError

使用python的abc模块可以是这个异常被更早的触发

 import abc

    class BasePizza(object):
        __metaclass__  = abc.ABCMeta

        @abc.abstractmethod
        def get_radius(self):
             """Method that should do something."""

使用abc和它的特殊类,如果你尝试实例化BasePizza或者继承它,都会得到TypeError错误

>>> BasePizza()
    Traceback (most recent call last):
      File "", line 1, in 
    TypeError: Can"t instantiate abstract class BasePizza with abstract methods get_radius

备注:我使用Python3.6实现的代码

In [8]: import abc
       ...:
       ...: class BasePizza(abc.ABC):
       ...:
       ...:     @abc.abstractmethod
       ...:     def get_radius(self):
       ...:          """:return"""
       ...:

    In [9]: BasePizza()
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
     in ()
    ----> 1 BasePizza()

    TypeError: Can"t instantiate abstract class BasePizza with abstract methods get_radius
混合静态,类和抽象方法

如果对Python编程、网络爬虫、机器学习、数据挖掘、web开发、人工智能、面试经验交流。感兴趣可以519970686,群内会有不定期的发放免费的资料链接,这些资料都是从各个技术网站搜集、整理出来的,如果你有好的学习资料可以私聊发我,我会注明出处之后分享给大家。

当需要创建类和继承时,如果你需要混合这些方法装饰器,这里有一些小窍门建议给你
记住要将方法声明为抽象,不要冻结这个方法的原型。意思是它(声明的方法)必须要执行,但是它在执行的时候,参数不会有任何限制

 import abc

        class BasePizza(object):
            __metaclass__  = abc.ABCMeta

            @abc.abstractmethod
            def get_ingredients(self):
                 """Returns the ingredient list."""

        class Calzone(BasePizza):
            def get_ingredients(self, with_egg=False):
                egg = Egg() if with_egg else None
                return self.ingredients + egg

这样是有效的,因为Calzone实现了我们为BasePizza定义的接口要求,这意味着我们也可以将它实现为一个类或者静态方法,例如:

import abc

        class BasePizza(object):
            __metaclass__  = abc.ABCMeta

            @abc.abstractmethod
            def get_ingredients(self):
                 """Returns the ingredient list."""

        class DietPizza(BasePizza):
            @staticmethod
            def get_ingredients():
                return None

这也是正确的,它实现了抽要BasePizza的要求,事实上是get_ingredioents方法不需要知道对象返回的结果,
因此,你不需要强制抽象方法实现成为常规方法、类或者静态方法。在python3中,可以将@staticmethod和@classmethod装饰器放在@abstractmethod上面

 import abc

        class BasePizza(object):
            __metaclass__  = abc.ABCMeta

            ingredient = ["cheese"]

            @classmethod
            @abc.abstractmethod
            def get_ingredients(cls):
                 """Returns the ingredient list."""
                 return cls.ingredients

和Java的接口相反,你可以在抽象方法中实现代码并通过super()调用它

  import abc

        class BasePizza(object):
            __metaclass__  = abc.ABCMeta

            default_ingredients = ["cheese"]

            @classmethod
            @abc.abstractmethod
            def get_ingredients(cls):
                 """Returns the ingredient list."""
                 return cls.default_ingredients

        class DietPizza(BasePizza):
            def get_ingredients(self):
                return ["egg"] + super(DietPizza, self).get_ingredients()

在上面的例子中,继承BasePizza来创建的每个Pizza都必须重写get_ingredients 方法,但是可以使用super()来获取default_ingredients

出处 https://blog.csdn.net/Stephen...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/44895.html

相关文章

  • Python标准库---6、内置函数(P-K-R-S-T-U)

    摘要:上一篇文章标准库内置函数下一篇文章标准库内置函数内置函数返回一个新的对象,可以选择带有从获取的元素。推荐使用内置函数来检测对象的类型,因为它会考虑子类的情况。上一篇文章标准库内置函数下一篇文章标准库内置函数 上一篇文章:Python标准库---5、内置函数(P-K-R)下一篇文章:Python标准库---7、内置函数(V-W-X-Y-Z) 内置函数: showImg(https://...

    lastSeries 评论0 收藏0
  • 资源大放送

    摘要:这是我收集的一些资源,分享给大家,全部放在百度网盘,有需要的请转存到自己的网盘或者下载,以免网盘链接失效,另外还有几百的视频文件存在网盘,需要的加全部分享在空间,自己可以去下载与权威指南配套源码禅意花园高清源码基础教程权威指南参考手册锋利 这是我收集的一些资源,分享给大家,全部放在百度网盘,有需要的请转存到自己的网盘或者下载,以免网盘链接失效,另外还有几百G的视频文件存在网盘,需要的加...

    lidashuang 评论0 收藏0
  • 第3章:抽象数据类型(ADT)和面向对象编程(OOP) 3.4面向对象编程(OOP)

    摘要:抽象数据类型的多个不同表示可以共存于同一个程序中,作为实现接口的不同类。封装和信息隐藏信息隐藏将精心设计的模块与不好的模块区分开来的唯一最重要的因素是其隐藏内部数据和其他模块的其他实施细节的程度。 大纲 面向对象的标准基本概念:对象,类,属性,方法和接口OOP的独特功能 封装和信息隐藏 继承和重写 多态性,子类型和重载 静态与动态分派 Java中一些重要的Object方法设计好的类面向...

    Heier 评论0 收藏0
  • JSbind方法与函数柯里化

    摘要:手贱搜了下函数柯里化,结果搜出腾讯的一篇反柯里化的文章中有趣的反柯里化技术,又牺牲不少脑细胞,赶紧吃饭,饿死了 原生bind方法 不同于jQuery中的bind方法只是简单的绑定事件函数,原生js中bind()方法略复杂,该方法上在ES5中被引入,大概就是IE9+等现代浏览器都支持了(有关ES5各项特性的支持情况戳这里ECMAScript 5 compatibility table),...

    wua_wua2012 评论0 收藏0

发表评论

0条评论

cheukyin

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<