Python系列(四)面向对象

Posted by YaPi on December 15, 2018

使用关键字class定义

在类里面编写函数和在模块里编写不同,需要在函数参数里面加上self。

class Student():
    # 类变量
    name = ''
    # 类变量
    age = 0
    
    # 实例方法/构造方法
    def __init__(self,name,age):
        # 实例变量
        self.age = age
        self.name = name
        # 实例变量和类变量不一样,是不同的
        self.xxxx_name = name
        
        print('init')
    # 实例方法
    def nom():
        pass
    
    # 类方法,调用可以用类名调用,也可以用实例调用
    @classmethod
    def plus_name(cls):
        pass
    
    # 静态方法
    # 没有默认强制传入一个参数
    # 实例可以调用、类也可以调用
     @staticmethod
    def static(self):
        pass
    

# 构造方法    
stu = Student('a',2)
stu.__init__()

# 会打印两次,可得两个结论
# __init__函数在初始化的时候会执行
# __init__函数可以单独被调用

对于模块来说

c = 50
def add(x, y):
    c = x + y
    print(c)

add(1, 2)
print(c)
# 返回 3 50
# 可得结论,局部变量不会改变全局变量得值

类实例变量获取规则

class Student:
    name = 'ggggg'

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


stu = Student(name='123', age=12)
print(stu.name)

输出

stu  ggggg
Student  ggggg

若是把name = name 改成 self.name = name
输出
stu  123
Student  ggggg

# 可得
实例变量stu获取name的时候,会先找自身保存的变量值,若没有,则会找类变量的值
类变量Student用点获取变量值时,始终都是获取的类变量

# 同时,类变量可用__class__获取

stuName = stu.__class__.name
成员可见性

通过对变量或者方法的名称前面加上双下划线设置可见性,如果有就是私有的,若没有,就是公开的

  1. 为什么__init__方法能够被调用呢?

这是因为python规定了方法名称以双下划线结尾的是可以访问的,即使它以双下划线开始。

  1. 为什么实例变量设置成了私有的,但是实例还是可以通过变量名获取呢?
实例有一个变量定义成了__score,在创建好实例过后,尝试给其赋值

stu = Student()
stu.__score = -1
print(stu.__score)

同样不会报错,也会输出

这是因为python的语言特性,它会认为是给实例stu新加了一个变量,而不是访问。

如果直接调用就会报错
stu = Student()
print(stu.__score)

通过实例调用__dict__方法可以查看所有的实例变量。

私有实例变量生成的是 : _类名+实例变量名称

继承

Python支持多继承

class Student(Human):
    def __init__(self,name,age):
        # 使用super关键字调用父类初始化方法
        super(Student,self).__init__(name,age)

枚举

首先引入一个枚举类 Enum,需要继承这个类

from enum import Enum

class VIP(Enum):
    Yellow = 1
    Green = 2
    Black = 3
    Red = 4

print(VIP.Yellow)
print(VIP.Yellow.name)
print(VIP.Yellow.value)

使用枚举可以有效的防止更改数值。也可以枚举名称重复,若重复类会报错。

不支持大小比较,支持 is 比较,支持等值比较

result = VIP.RED is VIP.RED
from enum import IntEnum,unique

# 只运行值为int类型,使用IntEnum
# 不允许值重复,加入unique,并使用注解

@unique
class VIP(IntEnum):
    Yellow = 1
    Green = 2
    Black = 3
    Red = 4

取值

print(VIP.Yellow) # VIP.Yellow
print(VIP.Yellow.name) # Yellow
print(VIP.Yellow.value) # 1
print(VIP[VIP.Yellow.name]) # VIP.Yellow
print(VIP[VIP.Yellow.name].name) # Yellow
print(VIP[VIP.Yellow.name].value) # 1

print(VIP(VIP.Yellow.value)) # VIP.Yellow
print(VIP(VIP.Yellow.value).name) # Yellow 
print(VIP(VIP.Yellow.value).value) # 1

闭包

闭包 = 函数 + 定义函数时候的环境变量(不能是全局变量,需要是调用函数所在的环境变量)

def curve_pre():
    a = 25
    def curve(x):
        return a * x * x
    return curve
    
# 得到闭包函数
f = curve_pre()
# 打印闭包函数相关信息
print(f.__closure__)

注:局部变量不会影响上级作用域对象的值

def f1():
    a = 10

    def f2():
        # 此时的a会被认为是一个局部变量,局部变量不影响外部变量的值
        a = 20
        print(a)

    print(a)
    f2()
    print(a)


f1()

输出:
10
20
10

次级作用域引用全局变量,使用global关键字

origin = 0

def go(step):
    global origin
    new_pos = origin + step
    origin = new_pos
    return new_pos

print(go(2)) => 2
print(go(3)) => 5
print(go(6)) => 11

次级作用域声明参数不是局部变量,使用nonlocal关键字定义,注意这儿是存在 step赋值操作,才会认为是i个局部变量,若只是引用,则不影响,不需要nonlocal申明

def go(step):
    def do(x):
        nonlocal step
        step += x
        return step

    return do


g = go(0)
print(g(2))
print(g(3))
print(g(5))
print(g.__closure__)