面向对象编程的三大特征:封装、继承和多态
类与实例的关系
类提供了把数据和功能绑定在一起的方式。创建新类时就等于创建了一种新的数据类型,这种数据类型称为对象类型
,根据该数据类型可以创新该类型的新实例。
例子:定义一种新的类为Cat类,该类即是我们
自定义的一种新数据类型
,根据Cat类可以创建许许多多的实例
。再比如:int数据类型有python提供,我们可以给出许许多多int类型的实例,比如100,200,300....注意:1. 基本数据类型有python提供,新的数据类型有我们自己自定义。
2. 创建实例对象有许多说法,如创建一个对象、实例化对象,把类实例化...
快速入门
定义一个类使用class关键字
class Cat:age = None,name = None,color = None
实例化对象以及修改属性名如下:
cat1 = Cat()
cat1.name = "小白"
cat1.age = 2
cat1.color = "白色"
如果要访问该对象,与修改属性名的语法相同,使用
对象名.属性名
,即cat1.name访问name属性
对象布尔值
Python所有对象都有一个布尔值,通过内置函数bool()可以获取对象的布尔值
- False、0、None、空字符串、空列表、空字典、空元组、空集合均为False
- 在if判断里,可以直接使用变量判断是否为True,如下操作:
content = "hello"
if content:print(content)
else:print("空字符串")
成员方法
类中定义的函数,称为成员方法
def 方法名(self, 形参列表):方法体
- 在方法定义的参数列表中,会有一个self
- self是定义成员方法时,需要写上的
- self表示当前对象本身
- 当我们通过对象调用方法时,self会隐式的传入
- 在方法内部,需要使用self,才能访问到成员变量
class Person:# 成员变量name = Noneage = None# 成员方法def hi(self):print("hi python")def cal01(self):result = 0for i in range(1, 1001):result += iprint(result)def cal02(self, n):result = 0for i in range(1, n+1):result += iprint(result)def get_sum(self, n1, n2):return n1 + n2;
除了以上这种定义成员方法的方法以外,
还有一种动态绑定方法,但是只能给实例化对象动态的添加方法
,如下代码:
def hi():print("hi python")class Person:name = Noneage = Nonep = Person()
p2 = Person()
# 动态的给p对象添加方法,只是给p添加方法,并不是给Person类添加方法
p.m1 = hi
p.m1()# p2.m1()会报错,因为没有动态的给p2添加方法
p2.m1()
self详解
如果在成员方法内,想要访问对象的成员变量或者调用其他的成员方法,那就得用self
class Cat:name = "波斯猫"def info(self, name):print(f"name信息: {name}")print(f"属性name: {self.name}")def eat(self):self.info()cat = Cat()
cat.info("小猫")
如果在成员方法内不想加self,那么就得在该方法上加上注解@staticmethod,就可以使该方法变成静态方法
- 静态方法不会接受隐式的第一个参数
- 静态方法可以不用实例化对象,直接使用类名就可以进行调用
class Dog:name = "藏獒"age = 2def info(self, name):print(f"name信息 -> {name}")# 静态方法@staticmethoddef ok():print("ok()....")dog = Dog()
dog.info("德牧")# 通过对象调用
dog.ok()
# 对过类名调用
Dog.ok()
self表示当前对象本身,也就是哪个对象调用,self就代表哪个对象
class Dog:name = "藏獒"age = 2def hi(self):# id为内存地址print(f"hi self: {id(self)}")dog2 = Dog()
# 查看dog2的两个id值是否相同,如果相同,说明self就是dog2
print(f"hi self: {id(dog2)}")
dog2.hi()
当我们通过对象调用方法时,self会隐式的传入
class Dog:name = "藏獒"age = 2def hi(self):# id为内存地址print(f"hi self: {id(self)}")dog2 = Dog()
# dog2在调用的时候,没有传入参数
dog2.hi()
# 可以理解为dog2.hi(dog2)
作用域
在面向对象编程中,主要的变量就是成员变量和局部变量
局部变量,一般是指在成员方法中定义的变量
成员变量的作用域在整个类中
,即该类的任何地方局部变量的作用域只能在该成员方法中
- 局部变量与成员变量可以重名,带上self即表示成员变量,反之表示局部变量
构造方法
如果想要创建类的对象时,就直接指定这个对象的属性
,而不是通过对象名.属性来设置的话,就得使用构造方法,基本语法如下:
def __init__(self,参数列表):代码...
在初始化对象时,会自动执行构造方法
,只要实例化一次对象,就会执行一次在初始化对象时,实参会自动传递给构造方法
构造方法是完成对象的初始化任务
一个类只有一个构造方法
,如果写了很多个,也只有最后一个生效Python支持动态生成对象的成员属性,可以不用在类里面定义成员属性
构造方法不准有返回值
class Person:name = Noneage = None# 把接收到的name和age赋给成员变量def __init(self, name, age):self.name = nameself.age = agep1 = Person("lwpigking", 20)
class Person:def __init(self, name, age):# 把接收到的name和age赋给成员变量self.name = nameself.age = agep1 = Person("lwpigking", 20)
封装
封装就是把抽象出的数据【成员属性】和对数据的操作【成员方法】封装在一起
,数据被保护在内部。程序只有通过被授权的操作,才能对数据进行访问。
封装的好处
可以将程序实现的细节隐藏起来
,比如有一个绘制柱状图的方法,该方法的代码非常庞大,但是我们只要调用这个方法,就可以画出柱状图- 可以对数据进行验证,保证合理
- 可以保护数据隐私,要求授权才可以访问
私有成员
通常情况下,类中的变量和方法都是共有的
,名称前都没有下划线
公共的变量和方法,在类的外部、类的内部,都可以正常访问
如何将属性、方法进行私有化?
类中的变量或方法以双下划线开头命名,则该变量或方法为私有的
,私有的变量或方法,只能在本类内部使用,类的外部无法使用如何访问私有属性和方法?
提供公共的方法,用于私有成员的操作
class Clerk:# 公共的属性name = None# 私有的属性__job = None__salary = Nonedef __init__(self, name, job, salary):self.name = nameself.__job = jobself.__salary = salarydef set_job(self, job):self.__job = jobdef get_job(self):return self.__job# 私有方法def __hi(self):print("hi")clerk = Clerk("tiger", "python工程师", 20000)
# 公共属性,在类的外部可以直接访问
print(clerk.name)
# 如果是私有属性,类的外部不可以直接访问
print(clerk.__job) # 这行执行不了,因为__job是私有的
继承
# 继承的基本语法
# 继承Person类
class Student(Person):<statement>
- 子类继承了所有的属性和方法,公有属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过
父类提供公共的方法
去访问- 如果子类和父类出现同名的成员,可以通过父类名、
super()
访问父类成员- 重写又称覆盖(
oevrride
),即子类继承父类的属性和方法后,根据业务需要,再重新定义同名的属性或方法
多态
多态即多种状态,不同的对象调用相同的方法,表现出不同的状态
,比如一个父类,有多个子类,不同的子类对象调用相同的方法,执行的时候产生不同的状态
抽象类
-
当父类的某些方法,需要声明,但是又不确定如何实现时
-
不需要实例化父类对象,父类主要的是用于设计和指定规范,让其它类来继承并实现
-
用@abstractmethod增加抽象方法,时该类变成抽象类
-
抽象类不能实例化