Python Rookie 扫盲 · 语法进阶

从头开始补习一下Python

[TOC]

面向对象

  • 类:抽象概念定义
    • 属性:用于描述对象
    • 方法:对象具有的行为
  • 对象:类的实例
1
2
3
4
5
6
class Apple:
    price = 100 # 可以直接定义类变量
print(Apple.price) # 使用类名进行调用

Apple.quality = 'good' # # 可以直接定义类变量
print(Apple.quality)

所有实例对象公用一个类变量:

1
2
3
4
5
6
7
class Apple:
    price = 100

apple = Apple()
print(apple.price) # 100
Apple.price = 200
print(apple.price) # 200

若定义类的方法,则必须要有一个必要参数:self,表示对象本身

1
2
3
4
5
6
7
class Apple:
    def eat(self):
        print('I am eating an apple')

apple = Apple()
apple.eat() # 'I am eating an apple'
Apple.eat(apple) # 'I am eating an apple'

其中实例可以有自己的属性(变量):

1
2
3
4
5
6
7
8
class Apple:
    def eat(self):
        print('I am eating an apple')

apple = Apple()
apple.size = 16 # 与类变量一致 可以在外部进行定义
print(apple.size) 
print(Apple.size) # 报错

但是这样的方法显然会让不同实例的属性不统一,需要进行自动化定义:

构造函数__init__()

1
2
3
4
5
6
class Apple:
    def __init__(self):
        self.eat = 'I am an apple!'

apple = Apple()
print(apple.eat)

同样可以带上参数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Apple:
    def __init__(self,size=12):
        self.eat = 'I am an apple!'
        self.size = size

apple = Apple()
print(apple.size)

apple2 = Apple(20)
print(apple2.size)

析构函数__del__()

1
2
3
4
5
6
7
8
9
class Apple:
    def __init__(self,size=12):
        self.eat = 'I am an apple!'
        self.size = size
    def __del__(self):
        print('Apple is being deleted!')

apple = Apple()
del apple

垃圾回收机制:

  • Python通过统计对象被引用次数(引用计数器),从而判断是否回收垃圾
  • 但是回收不是"立即"的, 由解释器在适当的时机,将垃圾对象占用的内存空间回收
  • 但是针对循环引用,会有额外的循环垃圾收集器

封装

  • 隐藏对象中不希望被外部访问的属性或方法

隐藏属性也称:魔术方法、特殊方法,通过开头结尾加上两个下划线:__

构造和析构都是一种隐藏属性

对于变量,可以在开头加个__,实现私有效果

1
2
3
4
5
6
class Apple:
    public_age = 10
    __private_age = 20

print(Apple.public_age)
print(Apple.__private_age) # 报错

但是似乎留了一个后门:_类名__隐藏属性

1
2
3
4
5
6
class Apple:
    public_age = 10
    __private_age = 20

print(Apple.public_age)
print(Apple._Apple__private_age)

标准的方式还是需要通过类的内部进行访问

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Apple:
    public_age = 10
    __private_age = 20

    def grow(self):
        self.__private_age += 1
    def print(self):
        print(self.__private_age)

apple = Apple()
apple.print()
apple.grow()
apple.print()

有了私有,当然还有保护变量:单个下划线

1
2
3
4
5
6
7
8
9
class Fruit:
    apple = True
    _banana = True
    __cherry = True

fruit = Fruit()
print(fruit.apple)      # True
print(fruit._banana)    # True
print(fruit.__cherry)   # AttributeError: 'Fruit' object has no attribute '__cherry'
  • 私有变量只能被类内部的方法进行使用
    • 建议别用,容易和python自带的属性冲突
    • 主要用于防止子类意外覆盖父类属性
  • 保护变量可以被外部使用
    • 建议只在内部使用,约定习惯,但是不禁止
  • 公开变量无限制
类型 示例 特点
公开变量 var 可以直接访问,无任何限制
保护变量 _var 本身与子类可以访问,不能被其他文件from import
私有变量 __var 只允许本身访问
魔术方法 __var__ 双下划线开头和结尾,Python的特殊方法

方法

  • 静态方法
1
2
3
4
5
6
7
8
9
class Animal:
    def speak(self, times):
        print("Animal speaks" * times)

    @staticmethod
    def eat(times):
        print("Animal eats" * times)

Animal.eat(3)

这样可以不创建实例直接用,同时不传self,减少内存

  • 类方法
1
2
3
4
5
6
7
class Animal:
    name = 'Animal'
    @classmethod
    def eat(cls):
        print(cls.name)

Animal.eat()

两种方法都可以用于规避实例的创建,静态方法不能访问类和实例,类方法可以访问类本身

方法 参数 自动传参 访问 被调用 用途
实例方法 self ✅ 类属性 / ✅ 实例属性 ✅ 实例 操作对象自身的状态
类方法 cls ✅ 类属性 / ❌ 实例属性 ✅ 类 / ✅ 实例 工厂方法、操作类级别的数据
静态方法 ❌ 类属性 / ❌ 实例属性 ✅ 类 / ✅ 实例 与类相关的工具函数,不依赖类或实例

这里的访问其实不是说无法访问,事实上类名.属性都可以访问

所以其实都是约定俗成

继承

1
2
3
4
5
6
7
8
class Fruit: # 其实默认继承object类(不然你以为默认自带的方法属性都是哪来的)
    size = 10

class Apple(Fruit):
    color = 'red' # 子类如果拓展了新的属性 ——> 派生类

apple = Apple()
print(apple.size)

重写

  • 子类中定义与父类相同名称的方法(覆盖)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Fruit:
    def print(self):
        print('I am a fruit')

class Apple(Fruit):
    def print(self):
        print('I am a apple')

apple = Apple()
apple.print()

重写可以用于拓展父类方法的功能:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Fruit:
    def print(self):
        print('I am a fruit')

class Apple(Fruit):
    def print(self):
        # Fruit.print(self) # 调用父类方法(需要传入self)
        super().print() # 更推荐这种方法
        # super(Apple, self).print() 等价
        print('I am a apple')

apple = Apple()
apple.print()

理解上把super()当成一个函数,返回的是父类,这样没什么问题

实际上是创建了一个super类的实例,动态查找MRO链,返回一个代理对象

MRO = MRO(Method Resolution Order,方法解析顺序)是Python中决定类继承关系中方法调用顺序的规则。它是Python多继承机制的核心组成部分。

方式 示例 区别
super() super().method() 动态查找 MRO 链,适用于多重继承
直接调用父类 Parent.method(self) 硬编码父类名,不适用于复杂继承

多继承

 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
class Father(object):
    def __init__(self):
        print("Father's __init__")
    def __del__(self):
        print("Father's __del__")
    def print(self):
        print("Father's print")

class Mother(object):
    def __init__(self):
        print("Mother's __init__")
    def __del__(self):
        print("Mother's __del__")
    def print(self):
        print("Mother's print")

class Child(Mother, Father):
    def __init__(self):
        super().__init__()
        print("Child's __init__")

    def __del__(self):
        super().__del__()
        print("Child's __del__")

child = Child()
child.print()

'''
Mother's __init__
Child's __init__
Mother's print
Mother's __del__
Child's __del__
'''

构造与析构的顺序其实可以根据super()去控制

这里print()super()默认都是就近的Mother(写在第一个)

调用其他父类需要显式的写出来

1
2
3
4
    def __init__(self):
        Mother.__init__(self)
        Father.__init__(self)
        print("Child's __init__")

目测不实现构造函数应该也会自动实现:

 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 Father(object):
    def __init__(self):
        print("Father's __init__")
    def __del__(self):
        print("Father's __del__")
    def print(self):
        print("Father's print")

class Mother(object):
    def __init__(self):
        print("Mother's __init__")
    def __del__(self):
        print("Mother's __del__")
    def print(self):
        print("Mother's print")

class Child(Mother, Father):
    pass

child = Child()

print(Child.__mro__) 
'''
Mother's __init__
(<class '__main__.Child'>, <class '__main__.Mother'>, <class '__main__.Father'>, <class 'object'>)
Mother's __del__
'''

多态

同一种行为多个表现形式

需要基于:

  • 继承
  • 重写
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Animal:
    def speak(self):
        print("Animal speaks")

class Dog(Animal):
    def speak(self):
        print("Dog barks")

class Cat(Animal):
    def speak(self):
        print("Cat meows")

dog = Dog()
dog.speak() # 重写

cat = Cat()
cat.speak() # 重写

这里其实就只是重写

1
2
3
4
5
def speak_loudly(animal: Animal):
    animal.speak()  # 多态

speak_loudly(Dog()) # 调用 Dog 的 speak 方法
speak_loudly(Cat()) # 调用 Cat 的 speak 方法

这样就可以体现多态

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计