1. 简介
对具象化的事物进行抽象, 尽管细节有偏差, 但与其他大部分编程语言的面向对象概念类似.
1.1 一个极简实例
class Animal(object):
def __init__(self, name, color):
self.name = name
self.color = color
def describe(self):
# return self.name + self.score
print(f'名字:{self.name} | 颜色:{ self.color}')
# 注意到__init__方法的第一个参数永远是self,表示创建的实例本身,
# 因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
# 构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值
xialing = Animal('黠灵', '灰白')
xialing.describe()
# 名字:黠灵 | 颜色:灰白
# Process finished with exit code 0
1.2 魔术方法 __init__
class Human(object):
def describe(self):
pass
xialing = Human()
xialing.name= "黠灵"
xialing.ability= "飞翔"
huban = Human()
xialing.name= "虎斑"
xialing.ability= "瞬移"
...
...
...
- 这其实是一个不是问题的问题, 初始化, 在其他语言和很多程序的设计中, 都会使用到.
- 比如人, 有些属性是需要通过学习和机遇才能获得, 想象科学家和普通人的区别.
- 有些属性则是人类天生就有的共性, 比如一出生就拥有了机体, 会睡觉, 会摄取营养.
- 很显然, 作为实例/个体的人, 有许多共性, 都具有姓名, 年龄, 族别, 性别等. 如果为每一个实例去一个个添加这些共同的属性, 会非常麻烦.
class Human(object):
def __init__(self, name, age, gender, nationality):
self.name = name
self.age = age
self.gender = gender
self.nationality = nationality
def describe(self):
pass
xialing = Human("黠灵", "6", "男", "汉") # 此时xialing 便具有所有的初始属性
print(xialing.name, xialing.age, xialing.gender, xialing.nationality )
望文生义 # initialization / initiate, 初始化,是一种特殊的方法。在创建对象时, 自动初始化对象,不需要手动/显式地调用. 即为对象成员变量赋初始值.
1.3 魔术方法 __new__
__init__ 的功能很明显, 其内部如何实现功能, 则与__new__相关.
Python 中实例化对象时, 不需要显式地使用new关键字 (其他语言比如PHP中则需要).
但实际上在实例化对象时, Python会自动效用__new__方法
class Animal(object):
def __init__(self):
print('init方法被执行')
def __new__(cls, *args, **kwargs):
print('new方法被执行')
print(cls) # cls 是 Animal 类
print(object.__new__(cls) ) # 得到 Animal 类的对象
return super().__new__(cls) # return object.__new__(cls) 这样写也行
xialing = Animal()
# new方法被执行
# init方法被执行
# Process finished with exit code 0
__new__ 先执行
__init__ 后执行
1.4 self 是什么?
class Person(object):
def ability(self):
print(self)
print('self的内存地址:' + str(id(self)))
laozi = Person()
laozi.ability()
print("实例化得到的对象的内存地址:" + str(id(laozi)))
zhuangzi = Person()
zhuangzi.ability()
print("实例化得到的对象的内存地址:" + str(id(zhuangzi)))
# <__main__.Person object at 0x00000233A6D5D750>
# self的内存地址:2420865619792
# 实例化得到的对象的内存地址:2420865619792
#
# <__main__.Person object at 0x00000233A6D5D7D0>
# self的内存地址:2420865619920
# 实例化得到的对象的内存地址:2420865619920
#
# Process finished with exit code 0
由此可知:
- self 和 当前对象指向相同的内存地址
- self 是 对象的引用
1.5 析构方法 __del__
# 当一个对象被删除或销毁时, Python 解释器会默认调用__del__()方法, 它称之为析构方法.
class Animal(object):
def __init__(self, name):
self.name = name
print("__init__ 方法被调用")
def __del__(self):
print("__del__ 方法被调用")
print(f'{self.name}对象被销毁')
cat = Animal("kitty")
# print(input("请输入"))
# __init__ 方法被调用
# __del__ 方法被调用
# kitty对象被销毁
# Process finished with exit code 0
2. 继承
2.1 单继承: 继承的最简实例
class Animal(object): # 创建一个基类
def run(self):
print('这是Animal基类内部的run方法')
class Dog(Animal): # 一个Dog子类, 继承自Animal. 子类能获得父类的全部功能, 称之为继承
pass
class Cat(Animal): # 一个cat子类
pass
xialing = Dog() # 将子类实例化, 得到对象xialing, 验证该对象是否获得基类的方法
xialing.run()
# 这是Animal基类内部的run方法
# Process finished with exit code 0
- Dog类继承自Animal类
- Dog类实例化生成的对象xialing获得了Animal类的方法
2.2 多继承
class Animal(object):
def climb(self):
print('动物会爬树')
class Human(object):
def talk(self):
print("人类会说话")
class Immortal(object):
def magic(self):
print("神仙会仙术")
class Monkey(Animal, Human, Immortal):
pass
wukong = Monkey()
wukong.climb()
wukong.talk()
wukong.magic()
# 动物会爬树
# 人类会说话
# 神仙会仙术
# Process finished with exit code 0
- Monkey类 继承了三个基类
- Monkey类实例化的对象获得了三个类的方法
2.2.1 多继承里的查找顺序问题
class Animal(object):
def climb(self):
print('动物会爬树')
class Human(Animal):
def climb(self):
print("人类会会爬树")
class Immortal(Animal):
def climb(self):
print("神仙也会爬树")
class Monkey(Human, Immortal):
pass
wukong = Monkey()
wukong.climb()
print(Monkey.__mro__)
# 人类会会爬树
# (<class '__main__.Monkey'>,
# <class '__main__.Human'>,
# <class '__main__.Immortal'>,
# <class '__main__.Animal'>,
# <class 'object'>)
# Process finished with exit code 0
- 自下往上, 由近及远的去查找climb方法. (注意monkey类的继承顺序, Human在前)
- 如果Monkey类里增加climb 方法, 并print(“猴子当然会爬树”), 那么最终的输出结果是猴子当然会爬树.
- 可通过 __mro__ 来查看python内部的查找顺序
- 这个涉及到语言本身的算法, 如广度优先.
2.2.2 方法重写
适用条件
- 参数列表必须完全和被重写方法的参数列表一致. 如果Human类的__init__方法中添加其他参数, 则会报错. TypeError: Animal.__init__() takes 3 positional arguments but 4 were given
- 返回类型必须完全和被重写方法的返回类型一致.
- 访问修饰符的限制一定要大于被重写方法的访问修饰符.
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
def describe(self):
print('动物会爬树')
class Human(Animal):
def __init__(self, name, age):
# Animal.__init__(self, name, age) # 手动调用
super().__init__(name, age) # super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间。
self.title = "齐天大圣" # 拓展其他的属性
def describe(self):
print(self.name)
print(self.age)
print(self.title)
wukong = Human('悟空', '99999')
wukong.describe()
# 悟空
# 99999
# 齐天大圣
# Process finished with exit code 0
2.2.3 方法重载
重载是让类以统一的方式处理不同类型数据的一种手段。
适用条件:
- 一个类里面
- 方法名字相同
- 参数不同
基本设计本原则:
- 仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载
解决的问题:
- 可变参数类型
- 可变参数个数
由此可知:
- 函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数.
- 函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。
进而得到:
- Python 里并没有语言规范上的方法重载.
- 但有其他语言里方法重载里的功能
- 因为Python函数自身的独特性和灵活性, 已经可以实现其他语言里的方法重载功能.
3. 多态
定义: 同一种行为 , 对于不同的子类对象有不同的行为表现
适用条件:
- 继承:多态必须发生在父类和子类之间
- 重写: 子类重写父类的方法
class Animal(object): # 创建一个基类
def describe(self):
print('这是Animal基类')
class Dog(Animal):
def describe(self):
print('这是继承Animal类的Dog类, 这里是狗')
class Cat(Animal): # 子类
def describe(self):
print('这是继承Animal类的Cat类, 这里是猫')
xialing = Dog()
xialing.describe()
lihua = Cat()
lihua.describe()
# 这是继承Animal类的Dog类, 这里是狗
# 这是继承Animal类的Cat类, 这里是猫
# Process finished with exit code 0
- 黠灵 之于Animal类, 得到了Dog子类的方法.
- 狸花 之于Animal类, 得到了Cat子类的方法.
- 同一个机器, 能根据需求制造出不同的产品
- Animal类 = 机床
- 重写和继承 = 调整和设计程序
- 第一次加工出齿轮/黠灵
- 第二次加工出叶片/狸花猫
- 重写和继承 = 调整和设计程序
- Animal类 = 机床
3.1 多态的优越性
class Animal(object): # 创建一个基类
def describe(self):
print('这是Animal基类')
class Dog(Animal):
def describe(self):
print('这是继承Animal类的Dog类, 这里是狗')
class Cat(Animal): # 子类
def describe(self):
print('这是继承Animal类的Cat类, 这里是猫')
class Bird(Animal): # 子类
def describe(self):
print('这是继承Animal类的Bird类, 这里是鸟')
def uniform_invoke(obj):
obj.describe()
obj_list = [Dog(), Cat(), Bird()]
for i in obj_list:
uniform_invoke(i)
# 这是继承Animal类的Dog类, 这里是狗
# 这是继承Animal类的Cat类, 这里是猫
# 这是继承Animal类的Bird类, 这里是鸟
#
# Process finished with exit code 0
这里需要一点想象力, 假设之后需要拓展功能代码, 直接增加bird类, 然后让如列表即可.
3.2 鸭子类型 duck typing
- 鸭子类型(英语:duck typing)是动态类型的一种风格。
- 在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由”当前方法和属性的集合”决定。
- “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
- “当看到一个人走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这个人就可以被称为鸭子。”
- 在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型。
class Animal(object): # 创建一个基类
def describe(self):
print('这是Animal基类')
class Dog(Animal):
def describe(self):
print('这是继承Animal类的Dog类, 这里是狗')
class Cat(Animal): # 子类
def describe(self):
print('这是继承Animal类的Cat类, 这里是猫')
class Bird(Animal): # 子类
def describe(self):
print('这是继承Animal类的Bird类, 这里是鸟')
#### 鸭子类型特性
class Human(object):
def describe(self):
print('这是基类Human')
class Student(Human):
def describe(self):
print('这是继承Human类的学生类, 这里是学生')
####
def uniform_invoke(obj):
obj.describe()
obj_list = [Dog(), Cat(), Bird(), Student()]
for i in obj_list:
uniform_invoke(i)
# 这是继承Animal类的Dog类, 这里是狗
# 这是继承Animal类的Cat类, 这里是猫
# 这是继承Animal类的Bird类, 这里是鸟
# 这是继承Human类的学生类, 这里是学生
#
# Process finished with exit code 0
只关注Student()对象本身, 而不管其属于Animal类还是Human类.
4. 封装
4.1 前置基础知识
4.1.1 前瞻与概括
- 实例方法,第一个参数必须要默认传实例对象,一般习惯用self。如果类调用实例方法,需要显示的传self, 也就是实例对象自己
-
def function_name(self, parm1, parm2)
-
- 类方法(由@classmethod装饰的方法),可以被类或类的实例对象调用。第一个参数必须要默认传类,一般习惯用cls。
-
@classmethod def function_name(cls):
-
- 静态方法,参数没有要求 (但可以带参数)。静态方法(由@staticmethod装饰的方法)
-
@staticmethod def function_name():
- 静态方法可以通过类对象和实例对象去访问, 但是很少使用实例对象去访问.
- 静态方法可通过类对象直接方法, 不需要额外的实例化操作, 减少代码和开销.
- 由于静态方法主要来存放逻辑性的代码,本身和类以及实例对象没有交互. 即在静态方法中,通常不会涉及到类中方法和属性的操作.
-
- 备注:
- 如果存在相同名称的 实例属性 和 类属性, 实例属性的优先级更高.
4.1.2 类属性和实例属性
类属性, 类对象和实例对象, 都可以访问
实例属性, 只有实例对象可以访问
class Animal(object):
name = "黠灵"
def __init__(self, age):
self.age = age
xialing = Animal(3)
print(xialing.name) # 实例对象 访问 类属性 √
print(xialing.age) # 实例对象 访问 实例属性 √
print(Animal.name) # 类对象 访问 类属性 √
# print(Animal.age) # 类对象 访问 实例属性 × # AttributeError: type object 'Animal' has no attribute 'age'
4.1.3 类方法
class Animal(object):
name = "黠灵" # 声明一个类属性
@classmethod # 运用 @classmethod 声明一个类方法
def describe(cls):
return cls.name
xialing = Animal()
print(xialing.describe()) # 实例对象 访问 类方法 √
print(Animal.describe()) # 类对象 访问 类方法 √
# 黠灵
# 黠灵
# Process finished with exit code 0
4.1.3.1 通过类方法修改类属性
class Animal(object):
name = "黠灵" # 声明一个类属性
@classmethod # 运用 @classmethod 声明一个类方法
def describe(cls):
return cls.name
@classmethod # 运用 @classmethod 声明一个类方法
def describe_modified(cls, parm):
cls.name = parm
xialing = Animal()
print(xialing.describe()) # 实例对象 访问 类方法 √ 黠灵
print(Animal.describe()) # 类对象 访问 类方法 √ 黠灵
Animal.describe_modified('狸花')
print(Animal.describe()) # 狸花
# 类方法可以修改类属性
4.1.4 静态方法
class Animal(object):
name = "黠灵" # 声明一个类属性
@classmethod # 运用 @classmethod 声明一个类方法
def describe(cls):
return cls.name
@staticmethod
def describe_again():
return Animal.name
xialing = Animal()
print(xialing.describe()) # 实例对象 访问 静态方法 √ 黠灵
print(Animal.describe()) # 类对象 访问 静态方法 √ 黠灵
print(xialing.describe_again()) # 实例对象 访问 静态方法 √ 黠灵 (一般不会这么做)
print(Animal.describe_again()) # 直接通过类对象 访问静态方法 √ 不需要实例化.
4.1.4.1 直接通过类对象 访问静态方法
import time
class TimeTest:
# def __init__(self,hour,min,second):
# self.hour=hour
# self.min = min
# self.second = second
@staticmethod
def showTime():
return time.strftime("%H:%M:%S",time.localtime())
pass
pass
print(TimeTest.showTime())
# t=TimeTest(2,10,15)
# print(t.showTime()) #没有必要通过这种方式去访问 静态方法
4.1.4.2 带参数的静态方法
class Demo(object):
@staticmethod
def summation(a, b):
print(a + b)
Demo.summation(1,2)
# 3
Process finished with exit code 0
4.2 什么是封装?
4.3 如何实现封装
4.3.1 私有属性
4.3.1.1 私有化的实例/类属性, 不能在类外部直接访问
没有使用私有属性封装的代码形式
class Animal(object):
def __init__(self):
self.name = "黠灵"
self.age = "6"
xialing = Animal()
print(xialing.name, xialing.age)
使用了私有属性封装的代码形式
class Animal(object):
def __init__(self):
self.__name = "黠灵" # 使用双下划线, 设置一个私有化属性__name
self.age = "9999"
# __str__ 将类的实例变成 str; print打印出来是个对象;使用了就把对象变成字符串
def __str__(self):
return f'狗名:{self.__name} | 狗命 {self.age}'
xialing = Animal()
print(xialing)
# print(xialing.name, xialing.age) # AttributeError: 'Animal' object has no attribute 'name'
如果不理解__str__, 可以看另一个实例
class Animal(object):
def __init__(self):
self.__name = "黠灵" # 设置一个私有化属性
self.age = "9999"
def get_data(self):
print(f'狗名:{self.__name} | 狗命 {self.age}')
xialing = Animal()
xialing.get_data() # 此时在类的外部, 实例对象, 通过类内部的get_data, 间接地访问__name
print(xialing.age) # 此时在类的外部, 实例对象, 直接访问类内部的age
#print(xialing.__name) # 此时在类的外部, 实例变量, 直接访问类内部的私有变量 __name (报错)
4.3.1.2 父类中私有化的实例/类属性, 不能被子类继承
class Animal(object):
def __init__(self):
self.__name = "黠灵" # 设置一个私有化属性
self.age = "9999"
# __str__ 将类的实例变成 str; print打印出来是个对象;使用了就把对象变成字符串
def __str__(self):
return f'狗名:{self.__name} | 狗命 {self.age}'
xialing = Animal()
print(xialing)
# print(xialing.name, xialing.age) # AttributeError: 'Animal' object has no attribute 'name'
class Cat(Animal):
pass
lihua = Cat()
print(lihua.__name) # AttributeError: 'Cat' object has no attribute '__name'
# 子类的Cat的实例化对象lihu 在类的外部, 无法访问__name 属性
4.3.1.3 私有化的实例/类属性, 可以在类的内部访问
以上代码已证实
4.3.2 私有方法
class Animal(object):
def fly(self):
print("fly")
def climb(self):
print('climb')
xialing = Animal()
xialing.fly(), xialing.climb() # 正常访问
class Animal(object):
def __fly(self): # 一个私有化方法
print("fly")
def climb(self):
print('climb')
xialing = Animal()
xialing.fly(), xialing.climb() # 报错
4.3.3 property方法
封装的目的不是为了完全的封闭, 而是为了更安全, 更高效的提供数据. 大多数情况下, 私有化的属性和方法, 还是要和外部进行交互. 正常情况, 可以在类的内部设计一个用于和外部交换的方法. 但除此之外, Python 还提供了一个专门的函数 property(). 直观和通俗的讲:
- 通常访问私有属性, 需要设置两个方法, 一个修改, 一个访问.
- 这种调用方式, “感觉”是调用一个方法, 而不是访问属性. 那么如何让调用者可以直接访问属性, 而且我们又能控制的方式 提供给调用者?
- property
- property 还可以和装饰器结合使用, 快速高效地实现一些功能
class Animal(object):
def __init__(self):
self.__age = 6
def get_data(self):
return self.__age
def set_data(self, value):
self.__age = value
age = property(get_data, set_data)
print(type(age))
xialing = Animal()
xialing.age = 999
print(xialing.age)
语法特性
- property 可以修饰类中属性的 get 、 set 和 del 方法,以及为属性对象创建文档字符串。
- 原型为 class property(fget=None, fset=None, fdel=None, doc=None) ,它返回 property 属性。
- fget 是获取属性值的函数。 fset 是用于设置属性值的函数。 fdel 是用于删除属性值的函数。 doc 为属性对象创建文档字符串。
本实例中
- xialing.age = 999 调用 setter
- xialing.age 调用 getter
4.3.4 单例模式
单例模式是常用和典型的”封装”形式和提现. Python里的单例模式有很多种实现方式, 比如模块, 装饰器, 类方法. 这里是通过魔术方法new.
- 当我们实例化一个对象时,是先执行了类的__new__方法,实例化对象.
- 然后再执行类的__init__方法,对这个对象进行初始化.
- 那么在new 和 init之间, 可以进行判断, 如果类里面具有某个属性, 则不再创建对象.
- 从而实现”一个类里面, 只有一个实例”.
class Animal(object):
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
xialing = Animal()
print(id(xialing))
huban = Animal()
print(id(huban))
# 1639090149520
# 1639090149648
# Process finished with exit code 0
可以看到生成两个类
class Animal(object):
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
if not hasattr(cls,'_instance' ):
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
xialing = Animal()
print(id(xialing))
huban = Animal()
print(id(huban))
# 2642697705424
# 2642697705424
# Process finished with exit code 0
单例模式的实现
4.4 动态绑定
4.4.1 动态绑定实例方法
import types
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
# def __str__(self):
# print(f'self.name | self.age')
def add_method(self):
print(f'{self.name} | {self.age}| {self.color}')
xialing = Animal('黠灵', '10')
# 动态增加属性 color
xialing.color = 'yellow'
# 动态绑定add_method方法
xialing.describe = types.MethodType(add_method, xialing)
# 调用动态绑定的方法
xialing.describe()
实例方法describe
4.4.2 动态绑定类方法
import types
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
# def __str__(self):
# print(f'self.name | self.age')
def add_method(self):
print(f'{self.name} | {self.age}| {self.color}')
@classmethod
def add_classmethod(cls):
print('Animal类 动态添加了 类方法describe_classmethod ')
Animal.describe_classmethod = add_classmethod
Animal.describe_classmethod()
# Animal类 动态添加了 类方法describe_classmethod
4.4.3 动态绑定静态方法
import types
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
# def __str__(self):
# print(f'self.name | self.age')
def add_method(self):
print(f'{self.name} | {self.age}| {self.color}')
@staticmethod
def add_staticmethod():
print('Animal类 动态添加了 静态方法describe_staticmethod ')
Animal.describe_staticmethod = add_staticmethod
Animal.describe_staticmethod()
# Animal类 动态添加了 静态方法describe_staticmethod
4.4.4 限制属性动态添加 __slot__()
class Animal(object):
__slots__ = ('name', 'age')
# __slots__ = ('name') # AttributeError: 'Animal' object has no attribute 'age'
def __init__(self, name, age):
self.name = name
self.age = age
def describe(self):
print(f'{self.name} | {self.age}')
xialing = Animal('黠灵', '10')
xialing.describe()
本站文章除单独注明外均为原创,本文链接https://bowmanjin.com/121,未经允许请勿转载。
请先
!