Python面向对象

Python面向对象

  • 思想
    • 用对象的角度描述世界
    • 以模块化思想解决工程问题
  • 常用名词
    • OO:面向对象(Object-Oriented)
    • OOA:面向对象分析(Object-Oriented Analysis)
    • OOD:面向对象设计(Object-Oriented Design)
    • OOP:面向对象编程(Object-Oriented Orogramming)
    • OOI:面向对象实现(Object-Oriented Implementation)

类&对象

类:抽象,描述的是一类共性事物的集合
对象:具象,描述的是某一类事物中的单个个体

  • 类定义
    • 格式:class ClassName:
    • 类名使用大驼峰命名法
    • 类中的函数一般需要self作为参数
    • 类包含属性(变量)和方法(函数)
      • 属性:事物的特征
      • 方法:事物的功能
  • 对象实例化
    • 格式:object_name = ClassName()
    • 对象名一般使用posix命名法
    • 访问对象中的变量或调用函数时,使用object_name.var/func()
  • 可以通过默认内置变量检查类和对象的所有成员
    • 格式:ClassName/obj_name.__dict__
    • 返回值:类或对象的成员信息,dict类型
  • 类在定义之后,可以不实例化对象,直接用类实例访问成员
    • 格式:ClassName.var/func()
# 类定义案例
 class Student(object):
    # pass用于占位,无意义
    pass
# 类定义和对象实例化案例
 class PythonStudent(object):
    name = 'Madman'
    age = 18
    course = 'Python'
    
    def learn_python(self):
        print('Learning Python...')
        return None
        
madman = PythonStudent()
print(madman)
print(madman.name)
print(madman.age)
print(madman.course)
madman.learn_python()

"""
运行结果:
<__main__.PythonStudent object at 0x000001FBEBF0DDA0>
Madman
18
Python
Learning Python...
"""
# 查看类和对象所有成员案例
class Person(object):
    name = 'no_name'
    age = 0
    
    def say_hello(self):
        print('Hello,World')

print(type(Person.__dict__))
print(Person.__dict__)

person = Person()
print(type(person.__dict__))
print(person.__dict__)

person.name = 'Madman'
person.age = 23
print(type(person.__dict__))
print(person.__dict__)

"""
运行结果:
<class 'mappingproxy'>
{'__module__': '__main__', 'name': 'no_name', 'age': 0, 'say_hello': <function Person.say_hello at 0x00000191156357B8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
<class 'dict'>
{}
<class 'dict'>
{'name': 'Madman', 'age': 23}
"""

类&对象成员分析

  • 类和对象都可以存储对象,成员可以归类所有,也可以归对象所有
  • 类存储成员时使用的是与类关联的一个对象
  • 对象存储成员时是存储在当前对象
  • 对象访问一个成员时,如果对象中没有该成员,就会尝试访问类中的同名成员,如果对象中有此成员,一定使用对象中的成员
  • 创建对象时,类中的成员不会放入对象当中,而是得到一个空对象,没有成员
  • 对象对类中成员重新赋值或者对象添加成员时,对应成员会保存在对象中,而不会修改类中的成员

self

  • self不是关键字
  • self可以用别的名称代替
  • 作用是指代自己本身
  • 基本所有的类方法都会带self,这样子能方便使用类自身的成员:self.var/func()
  • 如果通过对象调用一个方法,那么该对象会自动传入到当前方法的第一个参数中,如果通过类实例调用一个方法,则默认不传入任何参数,如果想要传入自身,需要手动将self传入:ClassName.func(self)
  • 有self形参的方法称为非绑定类的方法,可以通过对象访问,也可以通过类实例传入参数的方法访问;没有self形参的是绑定类的方法,只能通过类实例访问
# self案例1
class Person(object):
    def say_hi(self):
        print('Hello!')
        return None

madman = Person()
# 调用时,实际上默认实例作为第一个参数传入
madman.say_hi()
# 在已传入参数的情况下再传入一个参数,报错
madman.say_hi('haha')

"""
运行结果:
Hello!
TypeError: say_hi() takes 1 positional argument but 2 were given
"""
# self案例2
class Person(object):
    name = 'madman'
    
    def say_hi(haha):
        print('Hello! I am {0}'.format(haha.name))
        return None
    
madman = Person()
madman.say_hi()

"""
运行结果:
Hello! I am madman
"""
# self案例3
class Person(object):
    def say_hi(self):
        print('Hello,World')
    
    def say_hi_again():
        print('Hello,World!')

person = Person()

person.say_hi()

# 默认传入了一个参数self
person.say_hi_again()

# 类实例默认不传入参数
Person.say_hi()

# 手动传入自身参数不报错
Person.say_hi(self)

Person.say_hi_again()

# 强行传入自身参数报错
Person.say_hi_again(self)

"""
运行结果:
Hello,World
TypeError: say_hi_again() takes 0 positional arguments but 1 was given
TypeError: say_hi() missing 1 required positional argument: 'self'
NameError: name 'self' is not defined
Hello,World!
NameError: name 'self' is not defined
"""
# 鸭子模型案例
class Dog(object):
    name = 'NoNameDog'
    age = 0
    
    def __init__(self):
        self.name = 'little dog'
        self.age = 2
        
    def say_hi(self):
        print('I am {0}, I am {1} years old.'.format(self.name, self.age))
        
class Cat(object):
    name = 'NoNameCat'
    age = 0
    
dog = Dog()

# 此时,系统会默认把dog作为第一个参数c传入构造函数
dog.say_hi()

# 此时,self被dog对象替换
Dog.say_hi(dog)

# 此时,类实例Dog作为参数传入
Dog.say_hi(Dog)

# 此时,传入了类实例Cat
Dog.say_hi(Cat)

# 此时会报错
Dog.say_hi()

"""
运行结果:
I am little dog, I am 2 years old.
I am little dog, I am 2 years old.
I am NoNameDog, I am 0 years old.
I am NoNameCat, I am 0 years old.
TypeError: say_hi() missing 1 required positional argument: 'self'
"""

类属性和对象属性问题

  • 访问类属性格式:ClassName.var_name
  • 对象可以使用类的属性
  • 实例化一个对象时,实际上对象对类的属性进行了浅拷贝(即仅拷贝了类中变量的地址,而不拷贝值)
#  类属性和对象属性问题案例1
class Student(object):
    name = 'madman'
    age = 23
    
    def say_hi(self):
        print('My name is {0}, I am {1} years old.'.format(self.name, self.age))
        print(id(self.name), id(self.age))
        return None
 
# 查看类属性
print(Student.name, id(Student.name))
print(Student.age, id(Student.age))

madman = Student()
madman.say_hi()
# 实际上指向类的属性
print(id(madman.name), id(madman.age))

spiderman = Student()
spiderman.say_hi()
# 同样指向类的属性
print(id(spiderman.name), id(spiderman.age))

"""
运行结果:
madman 1855125198416
23 140726619730368
My name is madman, I am 23 years old.
1855125198416 140726619730368
1855125198416 140726619730368
My name is madman, I am 23 years old.
1855125198416 140726619730368
1855125198416 140726619730368
"""
  • 实例化的对象可以通过重新传入新的属性值来改变自身的属性值,但不改变类的属性(类属性和对象属性是两回事)
  • 再次证明实例化对象中属性实际上是对类中属性的浅拷贝
  • 如果对不同对象赋相同的属性值,他们的id一致(Python缓存机制)
# 类属性和对象属性问题案例2
class Student(object):
    name = 'madman'
    age = 23

    def say_hi(self, name, age):
        self.name = name
        self.age = age
        print('My name is {0}, I am {1} years old.'.format(self.name, self.age))
        print(id(self.name), id(self.age))
        return None

# 查看类属性
print(Student.name, id(Student.name))
print(Student.age, id(Student.age))
print('*' * 20)

# 对对象属性重新赋值
madman = Student()
madman.say_hi('badman', 18)
print(id(madman.name), id(madman.age))
print('*' * 20)

# 对象属性改变后,类属性仍不变
print(Student.name, id(Student.name))
print(Student.age, id(Student.age))
print('*' * 20)

# 如果给另一个对象赋相同的值,id是一样的
badman = Student()
badman.mame = 'badman'
badman.age = 19
print(badman.mame, id(badman.mame))
print(badman.age, id(badman.age))

"""
运行结果:
madman 2586910332496
23 140726554259904
********************
My name is badman, I am 18 years old.
2586912611720 140726554259744
2586912611720 140726554259744
********************
madman 2586910332496
23 140726554259904
********************
badman 2586912611720
19 140726554259776
"""

访问类的属性

  • 在类中只允许使用类的内容
  • 类访问不了对象的内容,对象能访问类的内容
  • 在类方法中如果强制访问类的属性,有两种方法
    • 方法一:__class__.var_name
    • 方法二:ClassName.var_name
# 访问类的属性案例
class Person(object):
    name = 'madman'
    age = 23
    local = 'Shenzhen'
    
    #  三种方式访问属性,第一种访问对象属性,第二第三种访问类属性
    def say_hi1(self):
        print('Hello! I am {0}, I am {1} years old and I live in {2}'.format(self.name, Person.age, __class__.local))
        print(id(self.name), id(Person.age), id(__class__.local))
        return None
    
    # 错误示范
    def say_hi2():
        print('Hello! I am {0}, I am {1} years old and I live in {2}'.format(self.name, Person.age, __class__.local))
        print(id(this.name), id(Person.age), id(__class__.local))
        return None
        
madman = Person()
madman.say_hi1()
print('*' * 20)
madman.say_hi2()
print('*' * 20)

badman = Person()
badman.name = 'badman'
badman.age = 18
badman.local = 'Huizhou'
badman.say_hi1()

"""
运行结果:
Hello! I am madman, I am 23 years old and I live in Shenzhen
2656137896768 140726553670080 2656137929328
********************
TypeError: say_hi2() takes 0 positional arguments but 1 was given
********************
Hello! I am badman, I am 23 years old and I live in Shenzhen
2656137896992 140726553670080 2656137929328
"""

构造函数

一种特殊的函数,类在实例化对象的时候,执行一些基础性的初始化工作

  • 使用特殊的名称和写法
    • 定义格式:def __init__(self):
    • 注意
      • 第一个参数必须要有,一般推荐用self
      • 不能有return
  • 在实例化对象的时候,自动执行构造函数,且构造函数是第一个被执行的函数
#  构造函数案例
class Student(object):
    name = 'NoName'
    age = 0

    def __init__(self):
        print('我是构造函数')

madman = Student()
print(madman.name)
print(madman.age)

"""
运行结果:
我是构造函数
NoName
0
"""

面向对象的三大特征

封装、继承、多态

封装

对类的成员进行访问限制

  • 封装的三个级别
    • public(公有)
      • 公有的封装实际对成员没有任何操作,任何地方都可以访问
      • 定义公有变量格式:var_name = vat_value
    • protected(保护)
      • 受保护的封装是将对象成员进行一定级别的封装,在类或者子类中都可以进行访问,但在外部不可以
      • 定义保护变量格式:_var_name = vat_value(一个下划线)
    • private(私有)
      • 最高级别的封装,只能在当前类或对象中访问
      • 定义私有变量格式:__var_name = var_value(两个下划线)
      • Python的私有不是真的私有,是一种称为name mangling的改名策略
      • 访问私有变量格式:object_name._ClassName__var_name
    • 注意:public、protected、private不是关键字
# 私有案例1
class Person(object):
    name = 'Madman'
    __age = 23
    
    def my_age(self):
        print(self.age)
        
person = Person()
print(person.name)

# 不能访问私有成员
print(person.__age)

# 但可以通过类内部访问
person.my_age()

print(Person.name)

# 类实例依然不能访问私有成员
print(Person.__age)

Person.my_age(Person)
"""
运行结果:
Madman
AttributeError: 'Person' object has no attribute '__age'
23
Madman
AttributeError: type object 'Person' has no attribute '__age'
23
"""
# 私有案例2
class Person(object):
    __age = 23
    
person = Person()

# 可以通过object_name._ClassName__var_name访问私有变量
print(person._Person__age)

# 直接访问就会失败
print(person.__age)

"""
运行结果:
23
AttributeError: 'Person' object has no attribute '__age'
"""

继承

一个类可以获得另一个类的所有成员

  • 作用
    • 减少代码量
    • 增加代码复用性
    • 设置类与类的关系
  • 继承与被继承
    • 继承与被继承是从属关系
    • 父类(基类):被子类继承的类,可以理解为超集
    • 子类(派生类):有继承行为的类,可以理解为子集
  • 继承的特征
    • 所有的类都继承自object类,即所有的类都是object类的子类
    • 子类一旦继承父类,则可以使用父类中除私有成员外的所有内容
    • 子类中可以定义独有的成员属性和方法
    • 子类继承父类后并没有将父类成员完全赋值到子类中,而是通过引用关系访问调用
    • 如果子类和父类之间有相同的属性名或行为名(包括构造函数),在实例化子类对象时优先使用子类的属性或行为
  • 继承类的格式:class SubClass(ParentClass)
# 继承案例1
# 默认继承object
class Animal1:
    pass

class Animal2(object):
    pass
# 继承案例2
class Person(object):
    name = 'NoName'
    _age = 0
    __local = 'NoLocal'

# 父类写在类定义中的括号里
class Teacher(Person):
    pass
    
t = Teacher()
# 子类可以继承父类的公有成员
print(t.name)
# 子类可以继承父类的保护成员
print(t._age)

# 但子类不可以继承父类的私有成员
print(t._Teacher__local)

"""
运行结果:
NoName
0
AttributeError: 'Teacher' object has no attribute '_Teacher__local'
"""
多继承

在Python中,子类允许有多个父类(多继承)

  • 但不鼓励使用多继承
  • 单继承和多继承的特点
    • 单继承
      • 优点:传承有序逻辑清晰语法简单隐患少
      • 缺点:功能不能无限扩展,只能在当前唯一的继承链中扩展
    • 多继承
      • 优点:类的功能扩展方便
      • 缺点:继承关系乱
  • 当多个父类之间有相同的属性名或者行为名时,子类按照顺序优先的原则去继承(继承最先出现的)
# 多继承案例
class Person(object):
    name = 'Person'
    
    def Walking(self):
        print('I can walking')
class Bird(object):
    name = 'Bird'
    
    def Flying(self):
        print('I can flying')

# 继承两个父类
class BirdMan(Person, Bird):
    pass
    
bm = BirdMan()
bm.Walking()
bm.Flying()

# 继承的父类间有相同的属性名,按照继承顺序优先的原则
print(bm.name)

"""
运行结果:
I can walking
I can flying
Person
"""
菱形继承/钻石继承问题

多个子类继承自同一个父类,而这些子类又被同一个类继承,继承关系图形成一个菱形图谱

# 菱形继承案例
class A(object):
    pass
    
class B(A):
    pass
    
class C(A):
    pass
    
class D(B, C):
    pass
多继承的MRO

MRO是在多继承中,用于保存继承顺序的一个列表

  • Python本身采用C3算法来处理多继承的菱形继承问题
  • MRO列表的计算原则
    • 子类永远在父类前面
    • 如果有多个父类,则根据继承语法中括号内类的书写顺序存放
    • 如果多个类继承了同一个父类,孙子类中只会选取继承语法括号中第一个父类的父类
子类继承父类成员的查找顺序问题
  • 优先查找本类的成员,如果本类中有定义,则使用本类的成员,如果本类没定义,则不断向上查找父类成员直到找到为止
# 子类继承父类成员的查找顺序问题案例
class Person(object):
    name = 'NoName'
    age = 0
    
class Madman(Person):
    name = 'Madman'
    
madman = Madman()
print(type(madman.name))
print(madman.name)
print(type(madman.age))
print(madman.age)

"""
运行结果:
<class 'str'>
Madman
<class 'int'>
0
"""
issubclass()

检测两个类之间是否是父子关系

  • 格式:issubclass(SubClass, ParentClass)
  • 返回值:布尔值
# issubclass()案例
class Person(object):
    pass

class Bird(object):
    pass

class BirdMan(Person, Bird):
    pass
    
class SuperBirdMan(BirdMan):
    pass
    
print(issubclass(BirdMan, Person))
print(issubclass(BirdMan, Bird))
print('*' * 20)
print(issubclass(Person, Bird))
print('*' * 20)
print(issubclass(Person, BirdMan))
print(issubclass(Bird, BirdMan))
print('*' * 20)
print(issubclass(SuperBirdMan, BirdMan))
print(issubclass(SuperBirdMan, Person))
print(issubclass(SuperBirdMan, Bird))

"""
运行结果:
True
True
********************
False
********************
False
False
********************
True
True
True
"""
isinstance()

检测一个对象是否是一个类的实例

  • 格式:isinstance(object_name, ClassName)
  • 返回值:布尔值
# isinstance案例
class Person(object):
    pass
    
person = Person()

print(isinstance(person, Person))
print(isinstance(Person, Person))

"""
运行结果:
True
False
"""
hasattr()

检测一个成员是否在类里面

  • 格式:hasattr(object_name, member_name)
  • 返回值:布尔值
# hasattr案例
class Perosn(object):
    name = 'Madman'
    def say(self):
        pass
        
person = Person()

print(hasattr(person, 'name'))
print(hasattr(person, 'age'))
print(hasattr(person, 'say'))

"""
运行结果:
True
False
True
"""
super
  • super不是一个函数,而是一个类
  • super的作用是获取MRO(MethodResolustionOrder)列表中的第一个类(往往是父类)
  • super与父类没有任何实质性关系,但通过super调用父类
mro
  • 用于查询某个类的父类
  • 格式:ClassName.__mro__
  • 元组类型的数据,显示所有的父类
# __mro__案例
class Animal(object):
    pass
    
class Dog(Animal):
    pass
    
print(Animal.__mro__)
print(Dog.__mro__)

"""
运行结果:
<class 'tuple'>
(<class '__main__.Animal'>, <class 'object'>)
<class 'tuple'>
(<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>)
"""
构造函数的继承

子类继承父类时,父类的构造函数默认被子类继承

  • 如果子类定义了构造函数,则实例化对象时使用自身的构造函数,不查找父类构造函数
  • 如果子类没定义构造函数,则按照MRO列表的顺序查找父类构造函数
  • 如果子类没定义构造函数且父类的构造函数带参数,则实例化对象时的参数应该按照父类参数实例化
# 构造函数的继承案例1
class Person(object):
    name = 'NoName'
    
    def __init__(self):
        print('My name is {0}'.format(self.name))
        
class Teacher(Person):
    pass
    
t = Teacher()

"""
运行结果:
My name is NoName
"""
# 构造函数的继承案例2
class Person(object):
    name = 'NoName'
    age = 0
    
    def __init__(self):
        print('My name is {0}'.format(self.name))
        
class Teacher(Person):
    age = 23
    
    def __init__(self, name):
        print('My name is {0}, I am {1} years old'.format(name, self.age))
        
p = Person()
# 子类和父类都有定义构造函数和相同的属性名,优先使用子类的构造函数和属性
t = Teacher('Madman')

"""
运行结果:
My name is NoName
My name is Madman, I am 23 years old
"""
# 构造函数的继承案例3
class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print('name: {0}, age: {1}'.format(self.name, self.age))
        
class Madman(Person):
    pass
    
# 使用的是Person类的构造函数,所以不输入对应的参数会报错
madman = Madman()

madman = Madman(name='madman', age=23)

"""
运行结果:
TypeError: __init__() missing 2 required positional arguments: 'name' and 'age'
name: madman, age: 23
"""
子类扩充父类方法问题
  • 子类如果想扩充父类的方法,可以在定义新方法的同时访问父类成员来进行代码重用,可以使用ParentClassName.parent_class_membersuper().parent_class_member格式来调用父类成员
# 子类扩充父类方法问题案例
class Person(object):
    def work(self):
        print('I can earn money!')
        
class Madman(Person):
    def earn_money(self):
        print('I can earn many money!!')
        
    def work_hard(self):
        # 用父类函数的功能进行扩充只需调用父类相应的函数
        # 用ParentClassName.parent_class_member格式调用父类函数
        # 注意要加self,因为类实例本身不自动传入参数
        Person.work(self)
        
        # 也可以用super().parent_class_member格式调用父类函数
        super().work()
        
        # 可以直接写代码扩充
        print('I can earn many money!!')
        
        # 也可以调用自身函数扩充
        self.earn_money()
        
madman = Madman()
madman.work_hard()

"""
运行结果:
I can earn money!
I can earn money!
I can earn many money!!
I can earn many money!!
"""

多态

同一个对象在不同情况下有不同的状态出现

  • 在Python中,多态不是语法,只是一种设计思想
  • 多态性:同一种调用方式有不同的执行结果
  • 多态:一类事物有多种形态
  • Mixin设计模式
    • 主要采用多继承方式对类的功能进行扩展
    • Mixin的目的就是给一个类增加多个功能,这样在设计类的时候,我们优先考虑通过多重继承来组合多个Mixin的功能,而不是设计多层次的复杂的继承关系
  • 使用Mixin实现多继承时需要注意
    • 职责必须单一,如果有多个功能,则写多个Mixin
    • Mixin不能依赖于子类的实现
    • 子类即使没有继承这个Mixin类也照样能工作,只是缺了某个功能
  • 优点
    • 使用Mixin可以在不对类进行任何修改的情况下扩充功能
    • 可以方便地组织和维护不同功能组件的划分
    • 可以根据需要任意调整功能类的组合
    • 可以避免创建很多新的类,导致类的继承混乱
# Mixin案例1(未遵循Mixin)
class Person(object):
    def say(self):
        print('I am a man')
        
class Student(Person):
     def learn(self):
        print('Learning')
        
class Teacher(Person):
    def work(self):
        print('Working')
        
class Tutor(Student, Teacher):
    pass
    
tutor = Tutor()

print(Tutor.__mro__)
print(tutor.__dict__)
print(Tutor.__dict__)

"""
运行结果:
(<class '__main__.Tutor'>, <class '__main__.Student'>, <class '__main__.Teacher'>, <class '__main__.Person'>, <class 'object'>)
{}
{'__module__': '__main__', '__doc__': None}
"""
# Mixin案例1(遵循Mixin)
class Person(object):
    def say(self):
        print('I am a man')
        
class Student(object):
     def learn(self):
        print('Learning')
        
class Teacher(object):
    def work(self):
        print('Working')
        
class Tutor(Person, Student, Teacher):
    pass
    
tutor = Tutor()

print(Tutor.__mro__)
print(tutor.__dict__)
print(Tutor.__dict__)

"""
(<class '__main__.Tutor'>, <class '__main__.Person'>, <class '__main__.Student'>, <class '__main__.Teacher'>, <class 'object'>)
{}
{'__module__': '__main__', '__doc__': None}
"""

property()

对属性的读、写、删除定义操作

  • 有三种方法使用类的成员描述符
    • 使用类实现描述器:适合多个类中的多个属性共用一个描述符
    • 使用属性修饰符: 适合在类中使用,控制一个类中的一个属性
    • 使用property函数:适用于当前类中,可以控制一个类中的多个属性
      • 格式:property(fget, fset, fdel, doc)
      • fget:获取属性时的操作
      • fset:修改或添加属性时的操作
      • fdel:删除属性时的操作
      • doc:说明文档
# property()案例
class Person(object):
    # 定义读操作
    def fget(self):
        return self._name * 2
        
    # 定义写操作
    def fset(self, name):
        self._name = name.upper()
        
    # 定义删除操作
    def fdel(self):
        self._name = 'NoName'
        
    name = property(fget, fset, fdel, '对name进行操作')
    
person = Person()

# 修改操作
person.name = 'Madman'
# 读操作
print(person.name)

"""
运行结果:
MADMANMADMAN
"""

类的内置属性

  • __dict__:以字典的形式显示类的成员组成
  • __doc__:获取类的文档信息
  • __name__:获取类的名称,如果在模块中使用,则获取模块的名称
  • __bases__:获取某个类的所有父类,以元组的方式显示
# 类的内置函数案例
class Person(object):
    '''
    这是一个说明文档
    '''
    name = 'Madman'
    age = 23
    
    def __init__(self):
        print('你好')
        
print(Person.__dict__)
print(Person.__doc__)
print(Person.__name__)
print(Person.__bases__)

"""
运行结果:
{'__module__': '__main__', '__doc__': '\n    这是一个说明文档\n    ', 'name': 'Madman', 'age': 23, '__init__': <function Person.__init__ at 0x000001AB980657B8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>}

    这是一个说明文档
    
Person
(<class 'object'>,)
"""

类的常用魔法函数

魔术方法就是不需要人为调用的方法,在特定的情况下自动触发

  • 魔术方法的统一特征是方法名前后都有两个下划线
  • 构造函数举例
    • 操作类相关
      • __init__:构造函数
      • __new__:对象实例化方法
      • __call__:对象被当作函数使用的时候触发
      • __str__:对象被当作字符串使用的时候触发
    • 描述符相关
      • __set__
      • __get__
      • __delete__
    • 属性操作相关
      • __getattr__:访问一个不存在的属性时触发
      • __setattr__:对成员属性进行设置的时候触发
        • 参数
          • self:用来获取当前对象
          • 第二个参数是被设置属性的名称,以字符串形式出现
          • 第三个参数是对属性设置的值
        • 作用
          • 进行属性设置的时候进行验证或修改
    • __gt__:进行大于判断时候触发的函数
      • 参数
        • self
        • 第二个参数是第二个对象名
      • 返回值:可以是任意值,推荐返回布尔值
# __call__案例
class A(object):
    def __init__(self):
        print('__init__:我被调用了')
    
class B(object):
    def __init__(self):
        print('__init__:我被调用了')
        
    def __call__(self):
        print('__call__:我被调用了')
        
a = A()
a()
b = B()
b()

"""
运行结果:
TypeError: 'A' object is not callable
__init__:我被调用了
__call__:我被调用了
"""
# __str__案例
class A(object):
    def __init__(self):
        print('__init__:我被调用了')
    
class B(object):
    def __init__(self): 
        print('__init__:我被调用了')

    def __str__(self):
        print('__str__:我被调用了')
        return '__str__:返回的内容'
        
a = A()
print(a)
b = B()
print(b)

"""
运行结果:
__init__:我被调用了
<__main__.A object at 0x000001D5240F08D0>
__init__:我被调用了
__str__:我被调用了
__str__:返回的内容
"""
# __getattr__案例
class A(object):
    name = 'NoName'
    
    def __init__(self):
        print('__init__:我被调用了')
        
class B(object):
    name = 'NoName'
    
    def __init__(self):
        print('__init__:我被调用了')
        
    def __getattr__(self, age):
        print('__getattr__:我被调用了')
        
a = A()
print(a.name)
print(a.age)

b = B()
print(b.name)
print(b.age)

"""
运行结果:
__init__:我被调用了
NoName
AttributeError: 'A' object has no attribute 'age'
__init__:我被调用了
NoName
__getattr__:我被调用了
None
"""
# __setattr__案例
class Person(object):
    def __setattr__(self, name, value):
        print('设置属性:{0}'.format(name))
        
        # 下面语句会导致死循环
        # self.name = value
        
        # 这种情况下,为了避免死循环,规定统一调用父类魔法函数
        super().__setattr__(name, value)
        
person = Person()
print(person.__dict__)
person.age = 18

"""
运行结果:
{}
设置属性:age
"""
# __gt__案例
class A(object):
    def __init__(self, name):
        self._name = name
        
a1 = A('one')
a2 = A('two')

print(a1 > a2)

class B(object):
    def __init__(self, name):
        self._name = name
        
    def __gt__(self, obj):
        print('{0}比{1}大吗?'.format(self, obj))
        
        return self._name > obj._name
        
b1 = B('one')
b2 = B('two')

print(b1 > b2)

"""
运行结果:
TypeError: '>' not supported between instances of 'A' and 'A'
one比two大吗?
False
"""

类和对象的三种方法

  • 实例方法
    • 需要实例化对象才能使用的方法,使用过程中可能需要借助对象的其他对象的方法完成
  • 静态方法
    • 不需要实例化,通过类直接访问
  • 类方法
    • 不需要实例化
# 三种方法的案例
class Person(object):
    # 实例方法
    def eat(self):
        print(self)
        print('Eating...')
    
    # 类方法
    # 类方法的第一个参数一般命名为cls,区别于self
    @classmethod
    def play(cls):
        print(cls)
        print('Playing...')
        
    # 静态方法
    # 不需要用第一个参数表示自身或类
    @staticmethod
    def say():
        print('Saying...')
        
madman = Person()

# 实例方法
Person.eat('eating')
madman.eat()

# 类方法
Person.play()
madman.play()

# 静态方法
Person.say()
madman.say()

"""
运行结果:
eating
Eating...
<__main__.Person object at 0x000001CACCBB4668>
Eating...
<class '__main__.Person'>
Playing...
<class '__main__.Person'>
Playing...
Saying...
Saying...
"""

抽象类

包括抽象方法的类叫抽象类

  • 抽象类的使用需要借助abc模块
  • 抽象方法:没有具体实现内容的方法
  • 抽象方法的主要作用是规范子类的行为和接口
  • 抽象类的主要作用是设定类的标准,让开发具有统一的规范
  • 抽象的使用
    • 抽象类可以包含抽象方法,也可以包含具体方法
    • 抽象类中可以有方法也可以有属性
    • 抽象类不允许直接实例化
    • 必须继承才可以使用,且继承的子类必须实现所有继承来的抽象方法
# 抽象类的实现案例
import abc

# 声明一个类并指定当前类的元类
class Human(metaclass=abc.ABCMeta):
    #  定义普通抽象方法
    @abc.abstractmethod
    def smoking(self):
        pass
        
    # 定义类抽象方法
    @abc.abstractmethod
    def drink():
        pass
        
    # 定义静态抽象方法
    @abc.abstractstaticmethod
    def play():
        pass

自定义类

  • 自定义类的方式
    • 通过定义函数和类,并把函数赋值给类
    • 借助MethodType()实现
    • 借助type()实现
    • 利用元类实现(MetaClass)
      • 元类是类
      • 元类用于创造别的类
# 函数名可以当变量使用案例
def say_hello(name):
    print('{0},你好!'.format(name))
    
say_hello('Madman')

say_hi = say_hello
say_hi('Madman')

"""
运行结果:
Madman,你好!
Madman,你好!
"""
# 类的组装案例1
class A(object):
    pass
    
def say(self):
    print('Saying...')

A.say = say

a = A()
a.say()

"""
运行结果:
Saying...
"""
# 类的组装案例2
from types import MethodType

class A():
    pass
    
def say(self):
    print('Saying...')
    
a = A()
a.say = MethodType(say, A)
a.say()

"""
运行结果:
Saying...
"""
# 类的组装案例3
def say(self):
    print('Saying...')
    
def talk(self):
    print('Talking...')
    
A = type('A', (object, ), {'say': say, 'talk': talk})

a = A()
a.say()
a.talk()

"""
运行结果:
Saying...
Talking...
"""
# 类的组装案例4

# 元类的写法是固定的,它必须继承自type
# 元类命名一般以MetaClass结尾
class AMetaClass(type):
    def __new__(cls, name, bases, attrs):
        print('我是元类')
        attrs['name'] = 'Madman'
        return type.__new__(cls, name, bases, attrs)
        
# 元类定义完就可以使用了
class B(object, metaclass=AMetaClass):
    pass
    
b = B()
print(b.name)

"""
运行结果:
我是元类
Madman
"""
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值