什么是面向对象?
面向对象是一种方法论,一种认识世界,分析世界的方法论。可以将万事万物抽象为各种对象,就是一个个的个体。
而各种对象具有的相同的特征,称为类。这些对象可以称之为一类对象,一类个体。
在计算机中“类”是“属性”和“方法”的集合。
比如:
狗咬人
我们把狗抽象为 狗类,这个狗类中包含了许多对象,它们都有相同的特征-都是狗。
把人抽象为 人类,这个人类中包含了许多对象,它们都有相同的特征-都是人。
咬人的这只狗是一个具体的对象,而狗类是一个抽象的概念。被咬的那个人,也是一个具体的对象,是一个个体。
我们用“狗类”可以描述所有狗,因为他们有相同的特征“都是狗”,这里的都是狗,可以看做这个类的属性。它们都会“睡觉”,“吃饭”,“咬人”这些便是它们的动作或者叫做方法。
狗类和人类本来没有关系,但是因为这个“咬人”的动作,狗和人就又联系到了一起。
面向对象3要素
1.封装
封装就是定义类。
函数的封装是为了“复用”,类的封装也是为了“复用”。另外也可以隐藏内部细节。
封装就是将“属性”和“动作”组装到一起,形成一个“类”。
只对外暴露一些接口,通过接口访问对象。
2.继承
子类可以从父类身上继承“属性”,“动作”。子类就不需要再定义了,就具有了,可以减少代码冗余。
比如:
人类有“身高,体重”可以“吃饭,睡觉”,这些是人类的属性和动作,人类实例化的一个个的对象,具体的人,同样继承了这些属性和动作。这样每次创建一个人,就不要在为他单独设置这些属性,因为他们已经继承到了。
同样的“人类”的这些属性,可以是继承于“动物类”,“生物类”。
比如:孩子他的遗传特征继承于他的父亲和母亲,这种称为多继承。
动物---哺乳类动物---灵长类动物----人类 这种是为单一继承。
- 多复用, 子类会继承父类的特征,子类就不需要单独设置了
- 多继承少修改
- OCP原则:如果要修改某个类的属性,应该先继承,在修改。
3.多态
动态绑定
类 class 定义
Python 类定义:
class ClassName: 语句块
------------------------
# encoding = utf-8 __author__ = "hugbg.com" class Person: """class Person ,定义人类""" x = 100 # 定义类属性 def eat(self): """ 类动作,吃, 类方法也是类属性 :return: """ print('abc') print(Person) # 类标识符 print(Person.__name__) # 类名字 print(Person.__doc__) # 类文档 print(Person.eat) # 类动作 print('----------------') print(Person.eat())
- class 关键字定义
- 大驼峰写法,本质上是一个“标识符”
- 类定义完成后,就产生了一个“类对象”,绑定到了标识符“ClassName”上。
类及类属性
- 类对象: 类也是对象,类的定义执行后会生成一个类对象
- 类属性: 类中定义的方法,和变量都是类属性。实例化后的对象,也会具有类属性
- 类变量: 属性也是标识符,也是变量。
Person中,x,eat 都是类的属性。__doc__, __name__ 是每个类都具有的特殊属性。
x 是类的属性,是这个类所具有的属性,只有这个类的实例才可以调用。
eat 是类的方法,本质上就是一个函数,它一般要求至少有一个参数,第一个形参通常是self(self是一个惯用标识符,可以换名字),这个self 代指当前实例本身。
实例化
a = Person() # 实例化 实例化可以创建一个真正的该类的对象。 因为同一个类,实例化后的每一个对象都是不同的,可以为每一个实例化对象,传递参数,也就是设置属性。
# encoding = utf-8 __author__ = "hugbg.com" class Person: """class Person ,定义人类""" # __new__(self) 实例化一个对象 def __init__(self, name, age=18): # 初始化函数 self.name = name self.age = age def showage(self): # 方法,方法中可以调用 初始化中的属性 return "{} is {}".format(self.name, self.age) # 这里调用的 self.name, self.age 就是前面初始化时绑定的这个实例的name属性和age属性。 tom = Person('Tom', 21) # 实例化一个tom jerry = Person('Jerry') # 实例化一个jerry print(tom.name, tom.age) # 打印 tom的属性 print(jerry.name, jerry.age) print(tom.showage()) # 调用tom的方法 print(jerry.showage())
python实例化后会调用__init__(self) 方法,进行初始化设置,可以不定义,如果不定义会在实例化后隐藏调用其父类的。
接收到Persion('Tom', 21) 后,__init__(self), 会进行一个实例的初始化,self此时就是 Persion('Tom', 21),__init__ 创建相应的属性self.name, self.age,返回一个实例对象,这个实例对象便被 “等号右边的 tom” 接收了。
其实在python 实例对象时,会先调用__new__(self) 方法,来创建一个对象,这个方法也可以进行自定义,这个方法完成后 self 就存在了,然后给__init__(self) 进行初始化配置。
__init__(self) 不能有返回值,只能return None
所以, self.name, self.age 又称为实例属性。它们是保存在实例中,而不是类中。实例属性可以修改添加。
方法绑定概念
类中调用实例的方法时,该方法会绑定到该实例,这样就不用传入self。
比如上面的 tom.showage() 调用时,实例tom调用类方法showage 时,会自动进行方法绑定。就是tom 会第一个注入到showage方法中,此时在showage中 tom就是self。self代指当前实例本身。
tom.showage 其实也会绑定
没有绑定,就没有注入。只有实例调用方法时,才会绑定。类调用类方法,不会绑定,就没有注入。
比如:
self是怎么存在的?
我们知道self代指实例本身,那么它到底是以什么形式存在的呢?
通过一个简单的例子,看下self是什么:
__init__() 中的self 其实就是实例化后的tom,调用方法是 传入的self也是实例本身tom。
self 代指实例本身。
在上面的例子中,tom.showage 会把类方法showage与实例tom绑定起来。
使用tom.showage() 则会把实例本身注入到showage方法中,也就是self。
实例变量和类变量
# encoding = utf-8 __author__ = "hugbg.com" class Person: age = 20 def __init__(self, name): self.name = name def showage(self): print(self.name, 'is',self.age) tom = Person('Tom') print(Person.age) # 类变量 - 20 tom.showage() # 实例变量未定义时,会使用类变量 - 20 print(tom.name, tom.age) # - 20 print() # 动态增加一个实例属性 tom.age += 5 # 此时相当于 定义了一个 实例变量, tom.age = 25, Person.age = 20 print('tom', tom.age) # 输出的是实例变量的值 - 25 print('Person', Person.age) # 类变量没有更改还是 20 tom.showage() # 调用的tom的age print('-' * 30) Person.age = 17 # 修改类变量为 17 jerry = Person('Jerry') # 新实例一个对象 print(Person.age) # 类变量已经是 17 print(jerry.name, 'is', jerry.age) # jerry.age 没有定义,使用的是Person.age - 17 print(tom.age) # tom.age 依然是前面定义的 25
通过实例可以访问类属性
另外一种访问方法:
通过实例访问类属性 __class__
通过类无法访问实例属性
类的特殊属性
特殊属性 | 含义 |
__name__ | 对象名 |
__class__ | 实例类对象 |
__dirt__ | 对象的属性的字典 |
__qualname__ | 类的限定名 |
特殊类属性 __class__
特殊类属性 __dirt__
类字典,保存公共的属性,方法
# encoding = utf-8 __author__ = "hugbg.com" class Person: x = 100 def __init__(self, name, age=18): self.name = name self.age = age def showage(self): return "{} is {}".format(self.name, self.age) print(*Person.__dict__.items(), sep='\n') # 类字典 __dict__ print('-' * 30)
实例字典,保存实例独有的属性
tom = Person('Tom', 20) print(*tom.__dict__.items(), sep='\n') # 实例字典
每个实例都会有一个单独的字典记录属性,所以各个实例的属性是不同的。我们访问的实例属性,其实就是在访问实例字典。
但是需要注意的是:
实例属性的查找顺序
实例使用 . 来访问属性,会现在__dict__中查找,如果没有,则通过__class__找到自己的类,然后再去类的__dict__中查找。
此时访问到了类属性后,也只是访问到了,并不会在自己的__dict__中添加属性。自由定义时候,才会添加。
练习
class Person: age = 3 height = 170 def __init__(self, name, age=18): self.name = name self.age = age tom = Person('Tom') # 实例化、初始化 jerry = Person('Jerry', 20) Person.age = 30 print(1, Person.age, tom.age, jerry.age) # 输出什么结果 30 18 20 print(2, Person.height, tom.height, jerry.height) # 输出什么结果 170 170 170 jerry.height = 175 print(3, Person.height, tom.height, jerry.height) # 输出什么结果 170 170 175 tom.height += 10 print(4, Person.height, tom.height, jerry.height) # 输出什么结果 170 180 175 Person.height += 15 print(5, Person.height, tom.height, jerry.height) # 输出什么结果 185 180 175 Person.weight = 70 print(6, Person.weight, tom.weight, jerry.weight) # 输出什么结果 70 70 70 print(7, tom.__dict__['height']) # 可以吗 180 print(8, tom.__dict__['weight']) # 可以吗 error
类变量可以使用全大写
类方法 classmethod
classmethod是内建函数之一,作为类方法可以使用实例和类访问,都有绑定,绑定了当前类本身。
调用方法时,会自动注入第一个参数,self 就是 class。
classmethod 方法会把类本身(type,__class__)作为第一个参数,注入到被装饰的函数中去。
如下:
使用classmethod装饰时,self 就是 class, 如下:
静态方法 staticmethod:
@staticmethod
与classmethod相反,它不会进行任何绑定,也就是没有任何注入
可以手动传参:
https://www.hugbg.com/archives/2401.html
评论