• 主页
  • 相册
  • 随笔
  • 目录
  • 存档
Total 244
Search AboutMe

  • 主页
  • 相册
  • 随笔
  • 目录
  • 存档

Python方法:实例、类、静态和抽象

2020-01-23

1. Overview

1
2
3
4
5
6
7
8
9
10
11
class MyClass:
def method(self):
return 'instance method called', self

@classmethod
def classmethod(cls):
return 'class method called', cls

@staticmethod
def staticmethod():
return 'static method called'

2. Instance Methods(实例方法)

You can see the method takes one parameter, self, which points to an instance of MyClass when the method is called. That’s the basic, no-frills method type you’ll use most of the time

  • can modify object state
  • can also access the class itself through the self.__class__ attribute

3. python2

1
2
3
4
5
6
>>> obj = MyClass()
>>> obj.method() # 这相当于语法糖,手动的方式(manually)是 MyClass.method(obj);或者说:python自动地把所有方法绑定到该类的实例上
('instance method called', <MyClass instance at 0x101a2f4c8>)

>>> MyClass.method()
TypeError: unbound method method() must be called with MyClass instance as first argument (got nothing instead)

This confirmed that method (the instance method) has access to the object instance (printed as <MyClass instance>) via the self argument

When the method is called, Python replaces the self argument with the instance object, obj

  • 注意未实例化会有“unbound method”报错
    • 其实就是没给self变量传值

4. python3

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

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> 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 0x7f8f18b52378>
>>> Pizza.get_size()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: get_size() missing 1 required positional argument: 'self'
>>>

5. Class Methods(类方法)

Instead of accepting a self parameter, class methods take a cls parameter that points to the class—and not the object instance—when the method is called.

Because the class method only has access to this cls argument, it can’t modify object instance state. That would require access to self. However, class methods can still modify class state that applies across all instances of the class.


Python 采用装饰器@classmethod来定义一个类方法


1
2
3
4
5
>>> obj.classmethod()
('class method called', <class MyClass at 0x101a2f4c8>)
# 不实例化也可以
>>> MyClass.classmethod()
('class method called', <class MyClass at 0x101a2f4c8>)

什么是类方法?类方法不是和一个对象绑定在一起,而是和…… 一个类!

无论你用什么方式访问这个方法,它总是会被绑定到它所绑定的类,它的第一个参数就是类本身(记住,类也是对象)

6. 用途

  • 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(继承).
  • Static methods calling static methods: if you split a static method 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 1
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
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)

6.1. cls()

1
2
3
4
5
6
7
8
9
class A(B): 
# Omitted bulk of irrelevant code in the class

def __init__(self, uid=None):
self.uid = str(uid)

@classmethod
def get(cls, uid):
o = cls(uid)

cls is the constructor function, it will construct class A and call the __init__(self, uid=None) function.

  • 类似o = A(uid)

If you enherit it (with C), the cls will hold ‘C’, (and not A)

7. Static Methods(静态方法)

a static method can neither modify object state nor class state. Static methods are restricted in what data they can access - and they’re primarily a way to namespace your methods


在开发中,我们常常需要定义一些方法,这些方法跟类有关,但在实现时并不需要引用类或者实例,例如,设置环境变量,修改另一个类的变量,等。这个时候,我们可以使用静态方法。
Python 使用装饰器@staticmethod来定义一个静态方法


1
2
3
4
5
>>> obj.staticmethod()
'static method called'
# 不实例化也可以
>>> MyClass.staticmethod()
'static method called'

8. 例子

Sometimes, you’ll write code that belongs to a class, but that doesn’t use the object itself at all.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Pizza(object):
@staticmethod
def mix_ingredients(x, y):
return x + y

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


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

8.1. 好处

  • Python 不需要为我们实例化的每个 Pizza 对象实例化一个绑定方法。绑定方法也是对象,而创建这些方法是有代价的。拥有一个静态方法就避免了这一点。
  • 它提升了代码的可读性:看到@staticmethod,我们知道这个方法不依赖于对象本身的状态。
  • 允许我们在子类中重写mix_ingredients方法
    x- 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.

一句话:静态方法牢牢地绑定在类上

9. 静态vs类

@staticmethod和@classmethod都可以直接类名.方法名()来调用


如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。

而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码

另外

The distinction between “self” and “cls” is defined in PEP 8 . As Adrien said, this is not a mandatory(强制). It’s a coding style.

  • self与cls只是pep8要求

10. 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.

抽象方法是在基类中定义的方法,但可能不提供任何实现


1
2
3
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.


一个抽象类可以被认为是其他类的蓝本。它允许你创建一组方法,这些方法必须在由抽象类构建的任何子类中创建。一个包含一个或多个抽象方法的类被称为抽象类。抽象方法是一个有声明但没有实现的方法。当我们在设计大型功能单元时,我们使用抽象类。当我们想为一个组件的不同实现提供一个共同的接口(API)时,我们使用抽象类

11. abc

ABC works by decorating methods of the base class as abstract and then registering concrete classes as implementations of the abstract base.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
import abc

class BasePizza(object):
__metaclass__ = abc.ABCMeta

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

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

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.

  • 相当于提供默认方法

12. 混合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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().

通过使用super()的默认机制来获取配料表

13. 其他

14. 方法与函数

As we get the basic understanding of the function and method both, let’s highlight the key differences between them

  • Unlike a function, methods are called on an object. Like in our example above we call our method .i.e. “my_method” on the object “cat” whereas the function “sum” is called without any object. Also, because the method is called on an object, it can access that data within it.
  • Unlike method which can alter the object’s state, python function doesn’t do this and normally operates on it.

In Short, a method is a function which belongs to an object.

15. 硬编码(写死)

硬编码(英语:Hard Code或Hard Coding)是指在软件实现上,将输出或输入的相关参数(例如:路径、输出的形式或格式)直接以常量的方式撰写在源代码中,而非在运行期间由外界指定的设置、资源、数据或格式做出适当回应。一般被认定是种反模式或不完美的实现,因为软件受到输入数据或输出格式的改变就必须修改源代码,对客户而言,改变源代码之外的小设置也许还比较容易。

但硬编码的状况也并非完全只有缺陷,因某些封装需要或软件本身的保护措施,有时是必要的手段。除此之外,有时候因应某些特殊的需求,制作出简单的应用程序,应用程序可能只会运行一次,或者永远只应付一种需求,利用硬编码来缩短开发的时间也是一种不错的决策

16. 参考资料

  • Python’s Instance, Class, and Static Methods Demystified – Real Python
  • Python 实例方法、类方法和静态方法_Leo的博客-CSDN博客
  • Difference between Method and Function in Python
  • python中的cls到底指的是什么,与self有什么区别? - 知乎
  • [The definitive guide on how to use static, class or abstract methods in Python](https://julien.danjou.info/guide-python-static-class-abstract-methods/
  • python - What does cls() function do inside a class method? - Stack Overflow
  • 硬编码 - 维基百科,自由的百科全书
  • Abstract Classes in Python - GeeksforGeeks
  • Program Language
  • Python
  • Advanced
Python厨书笔记-7
Python属性与特性
  1. 1. 1. Overview
  2. 2. 2. Instance Methods(实例方法)
    1. 2.1. 3. python2
    2. 2.2. 4. python3
  3. 3. 5. Class Methods(类方法)
    1. 3.1. 6. 用途
      1. 3.1.1. 6.1. cls()
  4. 4. 7. Static Methods(静态方法)
    1. 4.1. 8. 例子
      1. 4.1.1. 8.1. 好处
    2. 4.2. 9. 静态vs类
  5. 5. 10. Abstract methods(抽象方法)
    1. 5.1. 11. abc
  6. 6. 12. 混合
  7. 7. 13. 其他
    1. 7.1. 14. 方法与函数
    2. 7.2. 15. 硬编码(写死)
  8. 8. 16. 参考资料
© 2024 何决云 载入天数...