There are two paradigms of programming

  • imperative (using statements, loops, and functions as subroutines)

  • functional (using pure functions, higher-order functions, and recursion)

And another very popular paradigm is object-oriented programming (OOP), so let’s explore it.

Throughout this tutorial, I’ll walk you through the creation of a basic python object-oriented programming.

It’ll consist of some parts:

  • what is a class?
  • what is inheirance?

面向对象相关知识

  • 三大支柱:封装、继承、多态

    面向对象的设计原则:SOLID原则

    面向对象的设计模式:GoF设计模式(单例、工厂、代理、策略、迭代器)

    月薪结算系统 - 部门经理每月15000 程序员每小时200 销售员1800底薪加销售额5%提成

    例子:工资结算系统。

    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    """
    月薪结算系统 - 部门经理每月15000 程序员每小时200 销售员1800底薪加销售额5%提成
    """
    from abc import ABCMeta, abstractmethod


    class Employee(metaclass=ABCMeta):
    """员工(抽象类)"""

    def __init__(self, name):
    self.name = name

    @abstractmethod
    def get_salary(self):
    """结算月薪(抽象方法)"""
    pass


    class Manager(Employee):
    """部门经理"""

    def get_salary(self):
    return 15000.0


    class Programmer(Employee):
    """程序员"""

    def __init__(self, name, working_hour=0):
    self.working_hour = working_hour
    super().__init__(name)

    def get_salary(self):
    return 200.0 * self.working_hour


    class Salesman(Employee):
    """销售员"""

    def __init__(self, name, sales=0.0):
    self.sales = sales
    super().__init__(name)

    def get_salary(self):
    return 1800.0 + self.sales * 0.05


    class EmployeeFactory():
    """创建员工的工厂(工厂模式 - 通过工厂实现对象使用者和对象之间的解耦合)"""

    @staticmethod
    def create(emp_type, *args, **kwargs):
    """创建员工"""
    emp_type = emp_type.upper()
    emp = None
    if emp_type == 'M':
    emp = Manager(*args, **kwargs)
    elif emp_type == 'P':
    emp = Programmer(*args, **kwargs)
    elif emp_type == 'S':
    emp = Salesman(*args, **kwargs)
    return emp


    def main():
    """主函数"""
    emps = [
    EmployeeFactory.create('M', '曹操'),
    EmployeeFactory.create('P', '荀彧', 120),
    EmployeeFactory.create('P', '郭嘉', 85),
    EmployeeFactory.create('S', '典韦', 123000),
    ]
    for emp in emps:
    print('%s: %.2f元' % (emp.name, emp.get_salary()))


    if __name__ == '__main__':
    main()
  • 类与类之间的关系

    • is-a关系:继承
    • has-a关系:关联 / 聚合 / 合成
    • use-a关系:依赖

    例子:扑克游戏。

    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    """
    经验:符号常量总是优于字面常量,枚举类型是定义符号常量的最佳选择
    枚举 - 一个变量的值只有有限个选择,最适合的类型就是枚举
    通过枚举我们可以定义符号常量,符号常量优于字面常量
    """
    from enum import Enum, unique

    import random


    @unique
    class Suite(Enum):
    """花色"""

    SPADE, HEART, CLUB, DIAMOND = range(4)

    def __lt__(self, other):
    return self.value < other.value


    class Card():
    """牌"""

    def __init__(self, suite, face):
    """初始化方法"""
    self.suite = suite
    self.face = face

    def show(self):
    """显示牌面"""
    suites = ['♠️', '♥️', '♣️', '♦️']
    faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
    return f'{suites[self.suite.value]} {faces[self.face]}'

    def __str__(self):
    return self.show()

    def __repr__(self):
    return self.show()


    class Poker():
    """扑克"""

    def __init__(self):
    self.index = 0
    self.cards = [Card(suite, face)
    for suite in Suite
    for face in range(1, 14)]

    def shuffle(self):
    """洗牌(随机乱序)"""
    random.shuffle(self.cards)
    self.index = 0

    def deal(self):
    """发牌"""
    card = self.cards[self.index]
    self.index += 1
    return card

    @property
    def has_more(self):
    return self.index < len(self.cards)


    class Player():
    """玩家"""

    def __init__(self, name):
    self.name = name
    self.cards = []

    def get_one(self, card):
    """摸一张牌"""
    self.cards.append(card)

    def sort(self, comp=lambda card: (card.suite, card.face)):
    """整理手上的牌"""
    self.cards.sort(key=comp)


    def main():
    """主函数"""
    poker = Poker()
    poker.shuffle()
    players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')]
    while poker.has_more:
    for player in players:
    player.get_one(poker.deal())
    for player in players:
    player.sort()
    print(player.name, end=': ')
    print(player.cards)


    from enum import Enum, unique

    import random


    @unique
    class Suite(Enum):
    """花色(枚举)"""
    SPADE, HEART, CLUB, DIAMOND = range(4)

    def __lt__(self, other):
    return self.value < other.value


    class Card():
    """牌"""

    def __init__(self, suite, face):
    self.suite = suite
    self.face = face

    def __repr__(self):
    return self.__str__()

    def __str__(self):
    suites = ('♠️', '♥️', '♣️', '♦️')
    faces = ('', 'A', '2', '3', '4', '5', '6',
    '7', '8', '9', '10', 'J', 'Q', 'K')
    return f'{suites[self.suite.value]} {faces[self.face]}'


    class Poker():
    """扑克"""

    def __init__(self):
    self.index = 0
    self.cards = [Card(suite, face)
    for suite in Suite
    for face in range(1, 14)]

    def shuffle(self):
    """洗牌"""
    self.index = 0
    random.shuffle(self.cards)

    def deal(self):
    """发牌"""
    card = self.cards[self.index]
    self.index += 1
    return card

    @property
    def has_more(self):
    """是否有更多的牌"""
    return self.index < len(self.cards)


    class Player():
    """玩家"""

    def __init__(self, name):
    self.name = name
    self.cards = []

    def get_card(self, card):
    """摸牌"""
    self.cards.append(card)

    def arrange(self):
    """整理手上的牌"""
    self.cards.sort(key=lambda card: (card.suite, card.face))


    def main():
    """主函数"""
    poker = Poker()
    poker.shuffle()
    players = [
    Player('东邪'), Player('西毒'),
    Player('南帝'), Player('北丐')
    ]
    while poker.has_more:
    for player in players:
    player.get_card(poker.deal())
    for player in players:
    player.arrange()
    print(player.name, end=': ')
    print(player.cards)


    if __name__ == '__main__':
    main()

    说明:上面的代码中使用了Emoji字符来表示扑克牌的四种花色,在某些不支持Emoji字符的系统上可能无法显示。

  • 对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆)

垃圾回收、循环引用和弱引用

Python使用了自动化内存管理,这种管理机制以引用计数为基础,同时也引入了标记-清除分代收集两种机制为辅的策略。

1
2
3
4
5
6
typedef struct_object {
/* 引用计数 */
int ob_refcnt;
/* 对象指针 */
struct_typeobject *ob_type;
} PyObject;
1
2
3
4
5
6
7
8
/* 增加引用计数的宏定义 */
#define Py_INCREF(op) ((op)->ob_refcnt++)
/* 减少引用计数的宏定义 */
#define Py_DECREF(op) \ //减少计数
if (--(op)->ob_refcnt != 0) \
; \
else \
__Py_Dealloc((PyObject *)(op))

导致引用计数+1的情况:

  • 对象被创建,例如a = 23
  • 对象被引用,例如b = a
  • 对象被作为参数,传入到一个函数中,例如f(a)
  • 对象作为一个元素,存储在容器中,例如list1 = [a, a]

导致引用计数-1的情况:

  • 对象的别名被显式销毁,例如del a
  • 对象的别名被赋予新的对象,例如a = 24
  • 一个对象离开它的作用域,例如f函数执行完毕时,f函数中的局部变量(全局变量不会)
  • 对象所在的容器被销毁,或从容器中删除对象

引用计数可能会导致循环引用问题,而循环引用会导致内存泄露,如下面的代码所示。为了解决这个问题,Python中引入了“标记-清除”和“分代收集”。在创建一个对象的时候,对象被放在第一代中,如果在第一代的垃圾检查中对象存活了下来,该对象就会被放到第二代中,同理在第二代的垃圾检查中对象存活下来,该对象就会被放到第三代中。

1
2
3
4
5
6
7
# 循环引用会导致内存泄露 - Python除了引用技术还引入了标记清理和分代回收
# 在Python 3.6以前如果重写__del__魔术方法会导致循环引用处理失效
# 如果不想造成循环引用可以使用弱引用
list1 = []
list2 = []
list1.append(list2)
list2.append(list1)

以下情况会导致垃圾回收:

  • 调用gc.collect()
  • gc模块的计数器达到阀值
  • 程序退出

如果循环引用中两个对象都定义了__del__方法,gc模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的__del__方法,这个问题在Python 3.6中得到了解决。

也可以通过weakref模块构造弱引用的方式来解决循环引用的问题。

  • 魔法属性和方法(请参考《Python魔法方法指南》)

    有几个小问题请大家思考:

    • 自定义的对象能不能使用运算符做运算?
    • 自定义的对象能不能放到set中?能去重吗?
    • 自定义的对象能不能作为dict的键?
    • 自定义的对象能不能使用上下文语法?
  • 混入(Mixin)

    例子:自定义字典限制只有在指定的key不存在时才能在字典中设置键值对。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class SetOnceMappingMixin:
    """自定义混入类"""
    __slots__ = ()

    def __setitem__(self, key, value):
    if key in self:
    raise KeyError(str(key) + ' already set')
    return super().__setitem__(key, value)


    class SetOnceDict(SetOnceMappingMixin, dict):
    """自定义字典"""
    pass


    my_dict= SetOnceDict()
    try:
    my_dict['username'] = 'jackfrued'
    my_dict['username'] = 'hellokitty'
    except KeyError:
    pass
    print(my_dict)
  • 元编程和元类

    例子:用元类实现单例模式。

    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    import threading


    class SingletonMeta(type):
    """自定义元类"""

    def __init__(cls, *args, **kwargs):
    cls.__instance = None
    cls.__lock = threading.Lock()
    super().__init__(*args, **kwargs)

    def __call__(cls, *args, **kwargs):
    if cls.__instance is None:
    with cls.__lock:
    if cls.__instance is None:
    cls.__instance = super().__call__(*args, **kwargs)
    return cls.__instance


    class President(metaclass=SingletonMeta):
    """总统(单例类)"""

    def __init__(self, name, country):
    self.name = name
    self.country = country

    def __str__(self):
    return f'{self.country}: {self.name}'



    p1 = President('特朗普', '美国')
    p2 = President('奥巴马', '美国')
    p3 = President.__call__('克林顿', '美国')
    print(p1 == p2)
    print(p1 == p3)
    print(p1, p2, p3, sep='\n')
  • 面向对象设计原则

    • 单一职责原则 (SRP)- 一个类只做该做的事情(类的设计要高内聚)
    • 开闭原则 (OCP)- 软件实体应该对扩展开发对修改关闭
    • 依赖倒转原则(DIP)- 面向抽象编程(在弱类型语言中已经被弱化)
    • 里氏替换原则(LSP) - 任何时候可以用子类对象替换掉父类对象
    • 接口隔离原则(ISP)- 接口要小而专不要大而全(Python中没有接口的概念)
    • 合成聚合复用原则(CARP) - 优先使用强关联关系而不是继承关系复用代码
    • 最少知识原则(迪米特法则,LoD)- 不要给没有必然联系的对象发消息

    说明:上面加粗的字母放在一起称为面向对象的SOLID原则。

  • GoF设计模式

    • 创建型模式:单例、工厂、建造者、原型
    • 结构型模式:适配器、门面(外观)、代理
    • 行为型模式:迭代器、观察者、状态、策略

    例子:可插拔的哈希算法。

    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
    class StreamHasher():
    """哈希摘要生成器(策略模式)"""

    def __init__(self, alg='md5', size=4096):
    self.size = size
    alg = alg.lower()
    self.hasher = getattr(__import__('hashlib'), alg.lower())()

    def __call__(self, stream):
    return self.to_digest(stream)

    def to_digest(self, stream):
    """生成十六进制形式的摘要"""
    for buf in iter(lambda: stream.read(self.size), b''):
    self.hasher.update(buf)
    return self.hasher.hexdigest()

    def main():
    """主函数"""
    hasher1 = StreamHasher()
    with open('Python-3.7.1.tgz', 'rb') as stream:
    print(hasher1.to_digest(stream))
    hasher2 = StreamHasher('sha1')
    with open('Python-3.7.1.tgz', 'rb') as stream:
    print(hasher2(stream))

Classes

Objects are created using classes, which are actually the focal point of OOP.
The class describes what the object will be, but is separate from the object itself. In other words, a class can be described as an object’s blueprint, description, or definition.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Animal:
"""
This Code defines a class named Animal,which has three attrbutes:name,color and weight
"""
Count1 = 0
__Count2 = 0
## Class attributes are shared by all instances of the class.
## These can be accessed either from instances of the class, or the class itself.

def __init__(self, name, color, weight):
self.name = name
self.color = color
self._weight = weight ## private weight self.__
Animal.Count1 += 1
Animal.__Count2 += 1 ## private Count

## The __init__ method is the most important method in a class.
## This is called when an instance (object) of the class is created, using the class name as a function.
## The __init__ method is called the class constructor.

def showAnimal(self):
print("Total animals:", self.__count2, "\nName : ", self.name, "\nColor: ", self.color,
"\nWeight: ", self._weight, "kilograms.")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cat1 = Animal("Mr.MidNight", "black", 2.0)
cat2 = Animal("Garfield", "orange", 5.0)
cat1.showAnimal()
cat2.showAnimal()
# Name : Mr.MidNight
# Color: black
# Weight: 2.0 kilograms.
# Name : Garfield
# Color: orange
# Weight: 5.0 kilograms.

cat1.name
# Mr.MidNight
# cat1.weight
## 'Animal' object has no attribute 'weight'
cat1._weight
# 2.0
Animal.Count1
# 2
cat1._Animal__Count2
# 2
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
27
28
29
30
print(hasattr(cat1, 'color'), getattr(cat1, 'name', "None"))
## return none if attr not exists
# True Mr.MidNight
setattr(cat1, 'weight', 4.0)
cat1.showAnimal()
# Name : Mr.MidNight
# Color: black
# Weight: 2.0 kilograms.

## add/modify attrs
setattr(cat1, '_weight', 4.0) #== cat1._weight = "4.0"
cat1.showAnimal()
# Name : Mr.MidNight
# Color: black
# Weight: 4.0 kilograms.
setattr(cat1, '_Animal__Count2', 17)
cat1._Animal__Count2
# 17

cat = getattr(cat1, 'showAnimal') # 获取属性'power'
print(cat)
# <bound method Animal.showAnimal of <__main__.Animal object at 0x000000000531FC48>>
cat()
# Name : Mr.MidNight
# Color: black
# Weight: 2.0 kilograms.

delattr(cat1, 'color')
# cat1.showAnimal()
# AttributeError: 'Animal' object has no attribute 'color'
1
2
3
4
5
6
7
8
9
Animal
# __main__.Animal
cat1
# <__main__.Animal at 0x5459320>
print ("Animal.__doc__:", Animal.__doc__)
print ("Animal.__name__:", Animal.__name__)
print ("Animal.__module__:", Animal.__module__)
print ("Animal.__bases__:", Animal.__bases__)
print ("Animal.__dict__:", Animal.__dict__)
1
2
3
4
5
6
7
Animal.__doc__: 
This Code defines a class named Animal,which has three attrbutes:name,color and weight

Animal.__name__: Animal
Animal.__module__: __main__
Animal.__bases__: (<class 'object'>,)
Animal.__dict__: {'__module__': '__main__', '__doc__': '\n This Code defines a class named Animal,which has three attrbutes:name,color and weight\n ', 'Count1': 2, '_Animal__Count2': 2, '__init__': <function Animal.__init__ at 0x00000000052753A8>, 'showAnimal': <function Animal.showAnimal at 0x0000000005275438>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyError(Exception):

def __init__(self, value):
self.__value = value

def __str__(self):
return repr(self.__value)

try:
raise MyError("2" + 2)
except MyError as e:
print('My exception occurred, value:', e._MyError__value)
raise
except TypeError as err:
print(r"ERROR!!!", err)
# ERROR!!! can only concatenate str (not "int") to str

try:
raise MyError("2" * 2)
except MyError as e:
print('My exception occurred, value:', e._MyError__value)
raise
except TypeError as err:
print(r"ERROR!!!", err)
1
2
3
4
5
6
7
8
9
10
11
My exception occurred, value: 22
---------------------------------------------------------------------------
MyError Traceback (most recent call last)
<ipython-input-37-0e4488420dbd> in <module>
16
17 try:
---> 18 raise MyError("2" * 2)
19 except MyError as e:
20 print('My exception occurred, value:', e._MyError__value)

MyError: '22'

Setter & Getter

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
27
28
29
30
31
32
33
34
35
36
class Animal:
def __init__(self, name, weight):
self._name = name ## read only
self._weight = weight

@property
def name(self):
return self.name

@property
def weight(self):
return self._weight

@property
def kweight(self):
return self._weight/1000

@weight.setter
def weight(self, weight):
if not isinstance(weight, int):
raise ValueError('weight must be an integer!')
elif 0 <= weight <= 100:
self._weight = weight
return "%s's weight is %s now ." % (self._name, self.weight)
else:
raise ValueError('Too Fat')


lynx = Animal("Lorissa", "85")
# lynx.name = "Jonathan"
## AttributeError: can't set attribute
# lynx.weight = 5000
## ValueError: Too Fat
lynx.weight = 50
print(lynx.kweight)
# 0.05

Slots

That is to avoiding dynamically created attributes 😻

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
27
28
class Dog():
pass

dog = Dog()
dog.weight = 8
dog.size = "big" ## dynamically created attribute
print('dog.size =', dog.size)
# dog.size = big
print(dog.__dict__)
# {'weight': 8, 'size': 'big'}

class Animal():
__slots__ = ('name', 'color','_weight')
bird = Animal()
bird.name = 'cuckoo'
bird.color = "cornsilk"
bird._weight = 0.5

try:
bird.house = "tree"
except AttributeError as e:
print('AttributeError:', e)

# AttributeError: 'Animal' object has no attribute 'house'

# print(bird.__dict__)
## AttributeError: 'Animal' object has no attribute '__dict__'

Inheritance

Inheritance provides a way to share functionality between classes.

A class that inherits from another class is called a subclass.
A class that is inherited from is called a superclass.
If a class inherits from another with the same attributes or methods, it overrides them.

is-a、has-a、use-a

Inheritance

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
"""
多重继承 - 一个类有两个或者两个以上的父类
MRO - 方法解析顺序 - Method Resolution Order
当出现菱形继承(钻石继承)的时候,子类到底继承哪个父类的方法
Python 2.x - 深度优先搜索
Python 3.x - C3算法 - 类似于广度优先搜索
"""
class A():

def say_hello(self):
print('Hello, A')


class B(A):
pass


class C(A):

def say_hello(self):
print('Hello, C')


class D(B, C):
pass


class SetOnceMappingMixin():
"""自定义混入类"""
__slots__ = ()

def __setitem__(self, key, value):
if key in self:
raise KeyError(str(key) + ' already set')
return super().__setitem__(key, value)


class SetOnceDict(SetOnceMappingMixin, dict):
"""自定义字典"""
pass


def main():
print(D.mro())
# print(D.__mro__)
D().say_hello()
print(SetOnceDict.__mro__)
my_dict= SetOnceDict()
my_dict['username'] = 'jackfrued'
my_dict['username'] = 'hellokitty'


if __name__ == '__main__':
main()

Class Animal

Imagine several classes, Cat, Dog, Rabbit and so on.
Although they may differ in some ways (only Dog might have the method bark), they are likely to be similar in others (all having the attributes color and name). This similarity can be expressed by making them all inherit from a superclass Animal.

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class Animal:
weight = 1

def __init__(self, name, color):
self.name = name
self.color = color

def sayhi(self):
print("Hi,I'm a pretty %s" % self.name)

def getweight(self):
if Animal.weight >= 200:
return 'Big Animal'
elif Animal.weight >= 50:
return 'Medium Animal'
else:
return 'Small Animal'

def setweight(self, weight):
if not isinstance(weight, int):
raise ValueError('weight must be an integer!')
elif 0 <= weight <= 1000:
Animal.weight = weight
print('My new weight is ', Animal.weight)
else:
raise ValueError('Too Fat.')
````
**Animal Subclasses**

```python
class Wolf(Animal):

def bark(self):
print("Woof...")


class Dog(Wolf):
def bark(self): ## override wolf bark()
print("Bowwow!")

def original(self):
super().bark()


class Cat(Animal):
def __init__(self):
print("Cat")

def purrr(self):
print("Purrr...")

def meow(self):
print("Meow...")


class Pig(Animal):

def oink(self):
print("Oink!")
1
2
3
4
5
6
 ## somepig from Charlotte's Web
pig = Pig("somepig", "pink")
print(pig.color)
# pink
pig.oink()
# Oink!

Override & Superclass

If a class inherits from another with the same attributes or methods, it overrides(方法的覆盖或重写) them.

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
27
28
29
30
31
32
big_husky = Dog("luna", "grey")
# Dog
big_husky.name
# luna
big_husky.bark()
# Bowwow!
big_husky.original()
# Woof...

issubclass(Dog, Wolf)
# True
isinstance(Dog,Animal)
# True

big_husky.original()
# Woof...
big_husky.sayhi()
# Hi,I'm a pretty luna
big_husky.setweight(200)
# My new weight is 200
big_husky.getweight()
# Big Animal

## Without name & color
Kitty=Cat()
# Cat
Kitty.purrr()
# Purrr...
Kitty.setweight(50)
# My new weight is 50
Kitty.getweight()
# 'Medium Animal'

Polymorphism

The fancy word for the ability of multiple object types to implement the same functionality is polymorphism.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Animal():
def run(self):
print('Animal is running...')

class Wolf(Animal):

def run(self):
print('Dog is running...')

class Dog(Wolf):

def run(self):
print('Cat is running...')

def run(animal):
animal.run()

a = Animal()
w = Wolf()
d = Dog()
run(d)
# Cat is running...

Magic methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person(object):
"""人"""

def __init__(self, name, age):
self.name = name
self.age = age

# def __gt__(self, other):
# return self.name > other.name

def __str__(self):
return f'{self.name}: {self.age}'

def __repr__(self):
return self.__str__()
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
"""
魔术方法
如果要把自定义对象放到set或者用作dict的键
那么必须要重写__hash__和__eq__两个魔术方法
前者用来计算对象的哈希码,后者用来判断两个对象是否相同
哈希码不同的对象一定是不同的对象,但哈希码相同未必是相同的对象(哈希码冲撞)
所以在哈希码相同的时候还要通过__eq__来判定对象是否相同
"""


class Student():
__slots__ = ('stuid', 'name', 'gender')

def __init__(self, stuid, name):
self.stuid = stuid
self.name = name

def __hash__(self):
return hash(self.stuid) + hash(self.name)

def __eq__(self, other):
return self.stuid == other.stuid and \
self.name == other.name

def __str__(self):
return f'{self.stuid}: {self.name}'

def __repr__(self):
return self.__str__()


class School():

def __init__(self, name):
self.name = name
self.students = {}

def __setitem__(self, key, student):
self.students[key] = student

def __getitem__(self, key):
return self.students[key]


def main():
# students = set()
# students.add(Student(1001, '王大锤'))
# students.add(Student(1001, '王大锤'))
# students.add(Student(1001, '白元芳'))
# print(len(students))
# print(students)
stu = Student(1234, '骆昊')
stu.gender = 'Male'
# stu.birth = '1980-11-28'
print(stu.name, stu.birth)
school = School('千锋教育')
school[1001] = Student(1001, '王大锤')
school[1002] = Student(1002, '白元芳')
school[1003] = Student(1003, '白洁')
print(school[1002])
print(school[1003])


if __name__ == '__main__':
main()


Magic methods are special methods which have double underscores at the beginning and end of their names.

They are also known as dunders.
One common use of them is operator overloading.

This means defining operators for custom classes that allow operators such as + and * to be used on them.

1
2
'ABC'.__len__()
# 3
Methods Meaning
_sub_ -
_mul_ *
_truediv_ /
_floordiv_ //
_mod_ %
_pow_ **
_and_ &
_xor_ ^
_or_ |
_lt_ <
_le_ <=
_eq_ ==
_ne_ !=
_gt_ >
_ge_ >=
_len_ len()
_getitem_ indexing
_setitem_ assigning to indexed values
_delitem_ deleting indexed values
_iter_ iteration over objects (e.g., in loops)
_contains_ in
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
27
28
import random
class VagueList:

def __init__(self, cont):
self.cont = cont

def __getitem__(self, index):
return self.cont[index + random.randint(-1, 1)]

def __len__(self):
return random.randint(0, len(self.cont) * 2)

def __repr__(self):
return "VagueList({})".format(self.cont)

def __call__(self):
print('VagueList %s.' % self.cont)
# del ' VagueList' object is not callable

vague_list = VagueList(["A", "B", "C", "D", "E"])
vague_list()
# VagueList(['A', 'B', 'C', 'D', 'E'])
print(vague_list)
# VagueList(['A', 'B', 'C', 'D', 'E'])
print(len(vague_list)) ## # 0-10
# 7
print(vague_list[2]) ## # B-D
# D
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Vector:
def __init__(self, a = 0, b = 0):
self.a = a
self.b = b

def __str__(self):
return 'Vector(%d, %d)' % (self.a, self.b)

def __add__(self, other):
return Vector(self.a + other.a, self.b + other.b)

def __sub__(self, other):
return Vector(self.a - other.a, self.b - other.b)

def __del__(self):
class_name = self.__class__.__name__
print(class_name, "(", self.a, ", ", self.b, ") Destoryed!", sep = "")

v1 = Vector(2, 10)
v2 = Vector(5, -2)
v3 = Vector(9, -1)
print(v1 + v2 - v3)
Vector(2, 10) Destoryed!
Vector(5, -2) Destoryed!
Vector(9, -1) Destoryed!
Vector(7, 8) Destoryed!
Vector(-2, 9)
Vector(-2, 9) Destoryed!
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
27
class SpecialString:
def __init__(self, cont):
self.cont = cont

def __truediv__(self, other):
line = "=" * len(other.cont)
return "\n".join([self.cont, line, other.cont])

def __gt__(self, other):
for index in range(len(other.cont)+1):
res = other.cont[:index] + ">" + self.cont
res += ">" + other.cont[index:]
print(res)
h1 = SpecialString("Hello")
h2 = SpecialString("World!")
print(h1 / h2)
# Hello
# ======
# World!
h1 > h2
# >Hello>World!
# W>Hello>orld!
# Wo>Hello>rld!
# Wor>Hello>ld!
# Worl>Hello>d!
# World>Hello>!
# World!>Hello>

Types & Type

Types

1
2
3
4
5
6
7
8
9
10
11
12
13
import types
type('abc') == str
type(abs) == types.BuiltinFunctionType
type(lambda x: x) == types.LambdaType
type((x for x in range(10))) == types.GeneratorType

print('a is Animal?', isinstance(a, Animal)) ## True
print('a is Dog?', isinstance(a, Dog)) ## False
print('a is Wolf?', isinstance(a, Wolf)) ## False

print('d is Animal?', isinstance(d, Animal)) ## True
print('d is Dog?', isinstance(d, Dog)) ## True
print('d is Wolf?', isinstance(d, Wolf)) ## True
1
2
3
4
5
6
7
8
9
10
class cat():
pass

type(cat)
# type
type(cat())
# __main__.catprint(type(int))
kitty = cat()
type(kitty)
# __main__.cat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def hi(self, name = 'rabbit1'):
print('Hello, my name is %s.' % name)

attrs = {"strengths" :["Polite", "Cautious", "Responsible"], "Weaknesses":["Hesitant", "Timid", "Foolhardy"]}

rabbit = type('Rabbit', (), dict(attrs, hello = hi))
#== rabbit = type('Rabbit', (object,), dict(attrs, hello = hi))

Bugs_Bunny = rabbit()
print(type(Bugs_Bunny))
# <class '__main__.Rabbit'>
print(type(rabbit))
# <class 'type'>
Bugs_Bunny.hello("Bugs Bunny")
# Hello, my name is Bugs Bunny.
Bugs_Bunny.Weaknesses
# ['Hesitant', 'Timid', 'Foolhardy']

MethodType

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from types import MethodType
class Animal(object):
pass

cat = Animal()
cat.name = 'Lucas'

def set_name(self, name):
self.name = name

Animal.set_name = MethodType(set_name, Animal)
cat.set_name("Tom")

print(cat.name)
print(Animal.name)
# Lucas
# Tom

Class & Static Methods

Static Methods

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
27
28
29
30
31
32
33
from math import sqrt


class Triangle:

def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c

@staticmethod
def is_valid(a, b, c):
return a + b > c and b + c > a and a + c > b

def perimeter(self):
return self._a + self._b + self._c

def area(self):
half = self.perimeter() / 2
return sqrt(half * (half - self._a) *
(half - self._b) * (half - self._c))


a, b, c = 3, 4, 5

if Triangle.is_valid(a, b, c):
t = Triangle(a, b, c)
print(t.perimeter())
# print(Triangle.perimeter(t))
print(t.area())
# print(Triangle.area(t))
else:
print('Invalid Triangle.')

Pizza

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
27
28
29
30
31
32
33
34
35
36
37
38
class Pizza:

def __init__(self, toppings=None):
self.toppings = toppings
self._pineapple_allowed = False

@property
def pineapple_allowed(self):
return self._pineapple_allowed

@pineapple_allowed.setter
def pineapple_allowed(self, value):
if value:
password = input("Enter the password: ")
if password == "pizza!":
self._pineapple_allowed = value
else:
raise ValueError("Alert! Intruder!")

@staticmethod ## No cls self
def validate_topping(topping):
if topping == "bacon":
raise ValueError("No bacon!")
elif topping == "chicken breast":
print("Congratulations! You got the last chicken breast")
return True
else:
return True

@classmethod ## cls needed
def validate_topping2(cls, topping):
if topping == "pineapple":
raise ValueError("No pineapples!")
elif topping == "chicken breast":
print("Congratulations! You got the first chicken breast")
return True
else:
return True
1
2
3
4
5
6
7
8
ingredients = ["cheese", "onions", "spam", "eggs","chicken breast"]
pizza = Pizza(ingredients)
all(Pizza.validate_topping(i) for i in ingredients)
# Congratulations! you got the last chicken breast
# True
all(Pizza.validate_topping2(i) for i in ingredients)
# Congratulations! You got the first chicken breast
# True
1
2
3
pizza.pineapple_allowed = True
## Enter the password: pizza
# ValueError: Alert! Intruder!

MetaClass

1
2
3
4
5
6
7
8
9
10
11
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)

class MyList(list, metaclass=ListMetaclass):
pass
L = MyList()
L.add(1)
print(L)
# [1]

例子

假设小偷有一个背包,最多能装20公斤赃物,他闯入一户人家,发现如下表所示的物品。很显然,他不能把所有物品都装进背包,所以必须确定拿走哪些物品,留下哪些物品。

名称 价格(美元) 重量(kg)
电脑 200 20
收音机 20 4
175 10
花瓶 50 2
10 1
油画 90 9
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
"""
贪婪法:在对问题求解时,总是做出在当前看来是最好的选择,不追求最优解,快速找到满意解。
输入:
20 6
电脑 200 20
收音机 20 4
钟 175 10
花瓶 50 2
书 10 1
油画 90 9
"""
class Thing(object):
"""物品"""

def __init__(self, name, price, weight):
self.name = name
self.price = price
self.weight = weight

@property
def value(self):
"""价格重量比"""
return self.price / self.weight


def input_thing():
"""输入物品信息"""
name_str, price_str, weight_str = input().split()
return name_str, int(price_str), int(weight_str)

max_weight, num_of_things = map(int, input().split())
all_things = []
for _ in range(num_of_things):
all_things.append(Thing(*input_thing()))
all_things.sort(key=lambda x: x.value, reverse=True)
total_weight = 0
total_price = 0
for thing in all_things:
if total_weight + thing.weight <= max_weight:
print(f'小偷拿走了{thing.name}')
total_weight += thing.weight
total_price += thing.price
print(f'总价值: {total_price}美元')
1
2
3
4
5
6
7
8
9
10
11
12
20 6
电脑 200 20
收音机 20 4
钟 175 10
花瓶 50 2
书 10 1
油画 90 9
小偷拿走了花瓶
小偷拿走了钟
小偷拿走了书
小偷拿走了收音机
总价值: 255美元

REFERENCES