实例方法、静态方法、类方法、抽象方法

1.  Python中方法的工作方式(How methods work in Python)

A method is a function that is stored as a class attribute. You can declare and access such a function this way:

方法是一种函数,作为类的属性,存储于类中;可以用以下的方式声明和访问方法:

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

What Python tells you here, is that the attribute get_size of the class Pizza is a method that is unbound. What does this mean? We'll know as soon as we'll try to call it:

以上我们可以发现,类Pizza中的属性get_size是未绑定的方法。什么意思呢?我们调用以下这个方法之后就会明白:

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

We can't call it because it's not bound to any instance of Pizza. And a method wants an instance as its first argument (in Python 2 it must be an instance of that class; in Python 3 it could be anything). Let's try to do that then:

因为方法未绑定Pizza类型的实例对象,所以我们无法调用该方法。并且这个方法需要将实例作为第一参数(在Python2.x 中必须是以类实例的方式实现,而在Python3.x可以是任何形式)。让我们来试一下:

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

It worked! We called the method with an instance as its first argument, so everything's fine. But you will agree with me if I say this is not a very handy way to call methods; we have to refer to the class each time we want to call a method. And if we don't know what class is our object, this is not going to work for very long.

居然成功了!我们给第一参数传入一个实例对象,就能实现对方法的调用。但是这种调用方法的形式并不便捷——调用一个方法时同时每次必须引用这个类;要是我们不知道需要调用的对象(方法)属于哪个类时,那就真的懵逼了...

So what Python does for us, is that it binds all the methods from the class Pizza to any instance of this class. This means that the attribute get_size of an instance of Pizza is a bound method: a method for which the first argument will be the instance itself.

为了解决这个问题,Python将类Pizza中的所有方法绑定到实例化的Pizza类中,即实例化的类Pizza的属性get_size是一个绑定方法,实例化的方法的第一参数是实例对象自己。

>>> Pizza(42).get_size
<bound method Pizza.get_size of <__main__.Pizza object at 0x7f7c5ce803d0>>
>>> Pizza(42).get_size()
42
>>>

As expected, we don't have to provide any argument to get_size, since it's bound, its self argument is automatically set to our Pizza instance. Here's a even better proof of that:

不出所料,因为这种绑定关系,get_size方法的第一参数self默认被设置为实例化的Pizza对象,以下是更好的证据:

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

But what if you wanted to know which object this bound method is bound to? Here's a little trick:

要是你想知道,这种绑定方法绑定的是哪个对象,下面提供了一个小花招:

>>> m = Pizza(42).get_size
>>> m.__self__
<__main__.Pizza object at 0x7f7c5ce804d0>
>>> m == m.__self__.get_size
True
>>>

In Python 3, the functions attached to a class are not considered as unbound method anymore, but as simple functions, that are bound to an object if required. So the principle stays the same, the model is just simplified.

在Python3中,类中的函数仅仅是一个函数,不再考虑绑定方法;当函数需要时,可以绑定一个对象。因此相较于Python2,函数模型进行了简化,同时与Python2中函数的特性保持一致。

>>> class Pizza(object):
... def __init__(self, size):
... self.size = size
... def get_size(self):
... return self.size
...
>>> Pizza.get_size
<function Pizza.get_size at 0x7f307f984dd0>

2.  静态方法(Static method)

Static methods are a special case of methods. Sometimes, you'll write code that belongs to a class, but that doesn't use the object itself at all. For example:

静态方法是特殊的方法。有时,你想在类中写一些代码,却不去使用和这个类任何相关的东西。例如:

>>> class Pizza(object):
... @staticmethod
... def mix_ingredients(x, y):
... return x + y
... def cook(self):
... return self.mix_ingredients(self.cheese, self.vegetables)
...
>>>

In such a case, writing mix_ingredients as a non-static method would work too, but it would provide it a self argument that would not be used. Here, the decorator @staticmethod buys us several things:

以上的例子,完全可以写一个功能一样的非静态mix_ingredients方法替代,但是这样会把非必要的参数self引入。

使用装饰器@staticmethod有一下几个优点:

  • Python doesn't have to instantiate a bound-method for each Pizza object we instiantiate. Bound methods are objects too, and creating them has a cost. Having a static method avoids that:

Python没有必要为每个Pizza实例对象实例化一个绑定方法。毕竟绑定方法也是对象,创建这些对象同样会有消耗,定义静态方法可以避免这一情况:

>>> Pizza().cook is Pizza().cook
False
>>> Pizza().mix_ingredients is Pizza().mix_ingredients
True
>>> Pizza().mix_ingredients is Pizza.mix_ingredients
True
>>>
  • It eases the readability of the code: seeing @staticmethod, we know that the method does not depend on the state of object itself;

增加代码的可读性,静态方法完全独立于其所属的类 。

  • It allows us to override the mix_ingredients method in a subclass. If we used a function mix_ingredients defined at the top-level of our module, a class inheriting from Pizza wouldn't be able to change the way we mix ingredients for our pizza without overriding cook itself.

允许在子类中重写mix_ingredients方法。如果在模块。。。

3.  Class methods

Having said that, what are class methods? Class methods are methods that are not bound to an object, but to… a class!

什么是类方法?类方法是绑定在类上的方法,而不是绑定在对象上的方法。

>>> class Pizza(object):
... radius = 42
... @classmethod
... def get_radius(cls):
... return cls.radius
...
>>> Pizza.get_radius
<bound method type.get_radius of <class '__main__.Pizza'>>
>>> Pizza().get_radius
<bound method type.get_radius of <class '__main__.Pizza'>>
>>> Pizza.get_radius == Pizza().get_radius
True
>>> Pizza.get_radius is Pizza().get_radius
False
>>> Pizza.get_radius() == Pizza().get_radius()
True
>>> Pizza.get_radius()
42
>>>

Whatever the way you use to access this method, it will be always bound to the class it is attached to, and its first argument will be the class itself (remember that classes are objects too).

When to use this kind of methods? Well class methods are mostly useful for two types of methods:

  • Factory methods, that are used to create an instance for a class using for example some sort of pre-processing. If we use a @staticmethod instead, we would have to hardcode the Pizza class name in our function, making any class inheriting from Pizza unable to use our factory for its own use.
class Pizza(object):
def __init__(self, ingredients):
self.ingredients = ingredients @classmethod
def from_fridge(cls, fridge):
return cls(fridge.get_cheese() + fridge.get_vegetables())
  • Static methods calling static methods: if you split a static methods in several static methods, you shouldn't hard-code the class name but use class methods. Using this way to declare our method, the Pizza name is never directly referenced and inheritance and method overriding will work flawlessly
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies import math 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) p = Pizza(2, 5)
vol = p.get_volume()
print('vol = %f' %vol) '''vol = 62.831853'''

4.  Abstract methods

An abstract method is a method defined in a base class, but that may not provide any implementation. In Java, it would describe the methods of an interface.

So the simplest way to write an abstract method in Python is:

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

Any class inheriting from Pizza should implement and override the get_radius method, otherwise an exception would be raised.

This particular way of implementing abstract method has a drawback. If you write a class that inherits from Pizza and forget to implement get_radius, the error will only be raised when you'll try to use that method.

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

There's a way to triggers this way earlier, when the object is being instantiated, using the abc module that's provided with Python.

>>> import abc
>>> class BasePizza(object):
... __metaclass__ = abc.ABCMeta
... @abc.abstractmethod
... def get_radius(self):
... '''Method that should do something.'''
...
>>>

Using abc and its special class, as soon as you'll try to instantiate BasePizza or any class inheriting from it, you'll get a TypeError.

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

5.  Mixing static, class and abstract methods

When building classes and inheritances, the time will come where you will have to mix all these methods decorators. So here's some tips about it.

Keep in mind that declaring a method as being abstract, doesn't freeze the prototype of that method. That means that it must be implemented, but it can be implemented with any argument list.

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

This is valid, since Calzone fulfil the interface requirement we defined for BasePizza objects. That means that we could also implement it as being a class or a static method, for example:

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

This is also correct and fulfil the contract we have with our abstract BasePizza class. The fact that the get_ingredients method don't need to know about the object to return result is an implementation detail, not a criteria to have our contract fulfilled.

Therefore, you can't force an implementation of your abstract method to be a regular, class or static method, and arguably you shouldn't. Starting with Python 3 (this won't work as you would expect in Python 2, see issue5867), it's now possible to use the @staticmethod and @classmethod decorators on top of @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

Don't misread this: if you think this going to force your subclasses to implement get_ingredients as a class method, you are wrong. This simply implies that your implementation of get_ingredients in the BasePizza class is a class method.

An implementation in an abstract method? Yes! In Python, contrary to methods in Java interfaces, you can have code in your abstract methods and call it via 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()

In such a case, every pizza you will build by inheriting from BasePizza will have to override the get_ingredients method, but will be able to use the default mechanism to get the ingredient list by using super().

参考了: https://julien.danjou.info/blog/2013/guide-python-static-class-abstract-methods

instancemethod, staticmethod, classmethod & abstractmethod的更多相关文章

  1. Python staticmethod classmethod 普通方法 类变量 实例变量 cls self 概念与区别

    类变量 1.需要在一个类的各个对象间交互,即需要一个数据对象为整个类而非某个对象服务. 2.同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见. 3.有独立的存储区,属于整个类.   ...

  2. python 类中staticmethod,classmethod,普通方法

    1.staticmethod:静态方法和全局函数类似,但是通过类和对象调用. 2.classmethod:类方法和类相关的方法,第一个参数是class对象(不是实例对象).在python中class也 ...

  3. python staticmethod classmethod

    http://www.cnblogs.com/chenzehe/archive/2010/09/01/1814639.html classmethod:类方法staticmethod:静态方法 在py ...

  4. python中staticmethod classmethod及普通函数的区别

    staticmethod 基本上和一个全局函数差不多,只不过可以通过类或类的实例对象 (python里光说对象总是容易产生混淆, 因为什么都是对象,包括类,而实际上 类实例对象才是对应静态语言中所谓对 ...

  5. Python中的 @staticmethod@classmethod方法

    python类中有三种方法,常见的是实例方法,另外两种是staticmethod装饰的静态方法,和classmethod装饰的类方法. 1.对比 流畅的python里,用一个例子进行了对比: (1)两 ...

  6. python之内置装饰器(property/staticmethod/classmethod)

    python内置了property.staticmethod.classmethod三个装饰器,有时候我们也会用到,这里简单说明下 1.property 作用:顾名思义把函数装饰成属性 一般我们调用类 ...

  7. staticmethod classmethod

    1. 静态方法 @staticmethod 只是名义上归类管,实际上静态方法里访问不了类或者实例中的任何属性 2. 类方法 @classmethod 只能访问类变量,不能访问实例变量 3.属性方法 @ ...

  8. python staticmethod,classmethod方法的使用和区别以及property装饰器的作用

    class Kls(object): def __init__(self, data): self.data = data def printd(self): print(self.data) @st ...

  9. staticmethod classmethod修饰符

    一.staticmethod(function) Return a static method for function.A static method does not receive an imp ...

随机推荐

  1. 《Linux内核设计与实现》读书笔记(六)- 内核数据结构

    内核数据结构贯穿于整个内核代码中,这里介绍4个基本的内核数据结构. 利用这4个基本的数据结构,可以在编写内核代码时节约大量时间. 主要内容: 链表 队列 映射 红黑树 1. 链表 链表是linux内核 ...

  2. Dojo Style Guide

    Contents: General Quick Reference Naming Conventions Specific Naming Conventions Files Variables Lay ...

  3. 关于Android Studio中第三方jar包的Javadoc绑定

    原文地址:http://blog.csdn.net/a739697044/article/details/28116189   现在刚开始从Eclipse转用Android Studio,现在在尝试使 ...

  4. linux虚拟机与windows主机传输文件方法

    通过ssh    这种方法需要虚拟机内的linux安装ssh服务,默认是安装的。 首先检查ssh服务是开启的,通过shell下执行命令:service ssh status, 查看ssh服务是否已开启 ...

  5. Glib学习笔记(二)

    你将学到什么 如何实现Object的构造函数和析构函数 如何在条件检测不允许的情况下终止对象创建 Object的构造函数 对象的构造函数是不允许失败,如果你需要一个允许失败的GObject构造函数,使 ...

  6. javaweb访问hdfs的一些错误

    javaweb 与 HDFS 坑 前提:javaweb 项目,hdfs中的数据文件,导入访问hdfs的jar包,eclipse调试 问题:在×××.java代码中正常访问hdfs,浏览jsp时调用×× ...

  7. ubuntu - 官方简体中文wiki,有你想要的ubuntu的一切

    ubuntu官方简体中文wiki:http://wiki.ubuntu.org.cn/%E9%A6%96%E9%A1%B5

  8. go语言入门教程百度网盘 mysql图形化操作与数据导入

    mysql图形化操作与数据导入 @author:Davie 版权所有:北京千锋互联科技有限公司 数据库存储技术 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库.每个数据库都有一个 ...

  9. 【Linux】-Ubuntu常用命令吐血整理

    前言 刚刚接触Linux操作系统,真的是各种艰难啊,用个什么东西都得从头开始配置,这个时候才明白从头再来是什么滋味了.自己装了数个数十几次的Centos版本的Linux系统,好不容易争气了一次,跑了起 ...

  10. v-touch使用方法以及在项目中遇到的问题

    上篇博客中我记得还有一个坑没有解决好,在这篇博客中详细说明一下. 在 https://github.com/dreamITGirl/vuepageturn 我的这个代码库里,更新到2.1版本. 目前解 ...