目录
面向对象三大特性:封装、继承、多态
一、类与实例对象
1.声明类和实例化对象
python V2.X要求类继承原生object;python 3.X默认会添加原生Object类继承,无需手动添加;
# 声明类
class Person(object):
legs_num = 2 # 类属性
has_emotion = True
def play_fire(self): # 类方法
print('玩火')
def think(self):
print('思考')
# 类对象
print(Person)
# 创建实例对象;所谓对象,即一块内存空间
p1 = Person()
print(p1)
print(p1.legs_num)
p1.play_fire()
p2 = Person()
print(p2)
# 实例属性
p1.name = 'sxm'
print(p1.name)
p2.name = 'lisi'
print(p2.name)
del p1.name # 删除p1实例对象的name属性
print(p1.name)
2.对象的属性和初始化
在创建类的时候,可以手动添加__init__()方法,该方法是一个特殊的类实例方法,成为构造方法(或构造函数);__init__()方法可以包含多个参数,但必须包含一个self参数,且必须作为第一个参数;
# 定义Person类
class Person(object):
def __init__(self,name,age):
self.name=name # 实例属性
self.age=age
print(id(self))
# 实例化Person类的实例对象: 类实例对象=类名(实例化参数)
alvin=Person("alvin",18)
yuan=Person("yuan",22)
print(id(alvin))
注意:__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以将各种属性绑定到self,因此self就指向创建的实例本身;
3.实例方法
实例方法(或称为对象方法),指的是我们在类中定义的普通方法;只有实例化对象之后才可以使用的方法,该方法的第一个形参接收的一定是实例本身;
# 声明类
class Person(object):
legs_num = 2 # 类属性
has_emotion = True
def __init__(self, name, age):
self.name = name
self.age = age
print('self', self)
def play_fire(self): # 类方法
print('玩火')
def think(self):
print('思考')
p1 = Person('sxm', 12)
p1.play_fire() # p1实例对象调用play_fire()方法
4.一切皆对象
字符串,列表,字典等数据都是一个类,我们用的数据都是一个个的实例对象;区别就是,那些类是在解释器中注释好的,而我们使用的是自定义类;
5.类对象和类属性
# 声明一个类
class Student(object):
banji = '31' # 类属性,Student类的公有属性
def __init__(self, name):
self.name = name # 实例属性
def study(self, obj): # 实例方法
print('%s在向%s学习' % (self.name, obj.name))
# 创建实例对象
s1 = Student('sxm')
s2 = Student('sx')
s1.study(s2) # 传参也可以传实例对象
6.静态方法和类方法
类方法:使用装饰器@classmethod,第一个参数必须是当前类对象,该参数名一般约定为cls,通过他来传递类的属性和方法(不能传实例的属性和方法);
调用:类对象和实例对象都可以调用;
class Student():
# 类属性
cls_number = 68
@classmethod
def add_cls_number(cls):
cls.cls_number+=1
print(cls.cls_number)
Student.add_cls_number()
静态方法:使用装饰器@staticmethod,参数随意,没有self,cls参数,但是方法体重不能使用类或实例的任何属性和方法;
调用:类对象和实例对象都可以调用;
class Cal():
@staticmethod
def add(x,y):
return x+y
@staticmethod
def mul(x,y):
return x*y
cal=Cal()
print(cal.add(1, 4))
or
print(Cal.add(3,4))
二、类的简单继承
面向对象的编程带来的主要好处之一就是代码的重用,实现这种重用的方法之一就是通过继承机制,通过继承创建的新类或称为子类或称为派生类,被继承的类称为基类,父类或超类;
1.继承的简单使用
在继承关系中,继承者完全可以替换被继承者,反之则不可以,例如我们可以说猫是动物,但不能是动物是猫,对于这个,我们称之为"向上转型";
特点:
- 子类拥有父类非私有化的属性和方法
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
- 子类可以用自己的方式实现父类的方法
class Animal():
def sleep(self):
print('睡觉')
def eat(self):
print('吃饭')
class Cat(Animal): # Cat类继承Animal类
def catch_mouse(self):
print('抓老鼠')
c1 = Cat()
c1.sleep() # Cat的实力对象c1可以调用其父类Animal的sleep方法
2.重写父类方法和调用父类方法
重写父类方法:
# 父类方法的重写
class Animal():
def sleep(self):
print('睡觉...')
class Cat(Animal):
def sleep(self): #子类Cat中的sleep覆盖了父类Animal中的sleep方法
print('sleep...')
c1 = Cat()
c1.sleep()
父类方法的调用:
# 父类方法的调用
class Animal():
def sleep(self):
print('睡觉...')
class Cat(Animal):
def sleep(self):
# 方式1
# Animal.sleep(self)
# 方式2
# super(Cat,self).sleep()
# 或
super().sleep() # super()指的是父类的实例对象
print('sleep结束')
c1 = Cat()
c1.sleep()
练习:
# 最后的打印结果是?
class A():
def foo(self):
print('A foo...')
def bar(self):
print('bar...')
self.foo()
class B(A):
def foo(self):
print('B foo...')
b1 = B()
b1.bar()
# self.foo()调用的时候,调的是B类中的foo方法;因为self是b1
# 最后的打印结果是?
class A():
def foo(self):
print('A foo...')
def bar(self):
print('bar...')
self.foo()
class B(A):
def __init__(self, val):
self.foo = val
def foo(self):
print('B foo...')
b1 = B('abc')
b1.bar()
#解:self.foo()会报错,self.foo()会先找foo变量,找到的是构造函数中的'abc';字符串是不能调用的,最后会报错 str object is not callable
3.多重继承
如果在继承元组中列了一个以上的类,那么它就被称为"多重继承";语法如下:
class SubClassName (ParentClass1[, ParentClass2, ...]):
...
当基类 SubClassName有多个父类ParentClass1和ParentClass2时,多个父类中方法出现重写情况时,执行时,执行排列在前的父类中的方法;当存在继承链式关系时,使用C3算法;
4.type与isinstance的方法
5.dir()和__dict__属性对比python:dir()和__dict__对比_qq_39935684的博客-CSDN博客dir()内置函数用于实例对象的属性及方法列表;__dict__:返回实例对象的内置属性字典;# dir内置函数与__dict__()的区别class Student(): def __init__(self, name, sex): self.name = name self.sex = sex def test(self): passs1 = Student('sxm', 'male')print(dir(s1)) .https://blog.csdn.net/qq_39935684/article/details/122434347
三、面向对象之封装
封装指的是隐藏对象的属性和实现细节,仅对外提供公共访问方式;
追求"高内聚,低耦合"
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉
- 低耦合:仅对外暴露少量的方法用于使用
1.私有属性
class Student():
def __init__(self, name, score):
self.name = name
self.score = score
s1 = Student('sxm', 100)
print(s1.score)
s1.score = 59
print(s1.score)
在class内部的属性和方法,在class外部代码可以通过直接调用实例变量来操作数据;
私有属性:只有calss内部可以访问,外部不可以访问;
如果class外部代码需要访问私有变量score,可以定义get_score方法来访问;
如果calss外部代码需要修改私有变量score的值时,可以定义set_score方法;你也许会问,原先那种直接通过alvin.score = 100
也可以修改啊,为什么要定义一个方法大费周折?因为在方法中,可以设置值时做其他操作,比如记录操作日志,对参数做检查,避免传入无效的参数等等;
class Student():
def __init__(self, name, score):
self.name = name
self.__score = score
def get_score(self):
return self.__score
def set_score(self, score):
self.__score = score
s1 = Student('sxm', 100)
# class外部代码访问__score
print(s1.get_score())
# class外部代码重新赋值__score
s1.set_score(34)
print(s1.get_score())
对参数做检查:
class Student(object):
...
def set_score(self,score):
if isinstance(score,int) and 0 <= score <= 100:
self.__score = score
else:
raise ValueError('error!')
注意:
- 这种机制并没有真正意义上限制从外部直接访问属性,知道了类名和属性名就可以拼出名字来访问:_类名__属性名;
class Student(): def __init__(self): self.name = name self.__score = score s1 = Student('sxm', 98) print(s1.__dict__) # {'name': 'sxm', '_Student__score': 98} # 相当于在s1实例化的时候,将_Student__score = 98写入到了s1的实例对象所在内存空间中
- 变形的过程只在类的内部生效,在类外部定义后的赋值操作 ,变量名不会变形
问:最后的s1.get_score()获取到的值是多少? class Student(): def __init__(self, name, score): self.name = name self.__score = score # 实例化的过程中,写入到s1实例对象内存空间中的变量名是_Student__score def get_score(self): return self.__score def set_score(self, score): self.__score = score #读取时读取的是s1实例对象内存空间中_Student__score变量 s1 = Student('sxm', 100) s1.__score = 34 #写入到s1内存空间时,写入的是__score变量名 print(s1.get_score()) #_Student__score变量与__score变量不冲突,所以最后获取到的值是100
- 单下划线、双下划线、首尾双下划线说明:
__foo__:定义的是特殊方法,一般是系统定义的名字,例如:__init__()
_foo:以单下划线开头的是protected类型的变量,即保护类型只能允许其本身与子类进行访问;(约定俗成,不限语法)
__foo:双下划线表示有私有类型(private)的变量,只允许类本身访问;
2.私有方法
在继承关系中,父类如果不想让子类覆盖自己的方法,可以将方法定义成私有的;
# 问c1.test()返回的结果是?
class Person():
def __speak(self): # 存储的是_Person__speak()方法
print('speak...')
def test(self): # 在Person类内部,读取的是_Person__speak()方法
self.__speak()
class Child(Person):
def __speak(self): # 存储的是_Child__speak()方法
print('咿呀学语...')
c1 = Child()
c1.test()
# 最后调用的是父类的__speak方法