6.Python基础学习笔记day6-面向对象
一、类与对象
'''
类:一个具有特殊功能实体的集合
对象:在一个类中,一个具有特殊功能的实体,能够帮忙解决特定的问题,
对象通常情况下我们也称之为实例
类与对象的关系:类是描述某一对象的共同特征,而对象是类的具体体现。
1.类是对象的抽象,对象是类的具体体现
2.自己定义类也是一种数据类型类似str,list,类本身并不占用内存,
占用内存的是实例化出来的对象。
例如:
快递 类
顺丰/圆通 对象
饭 类
面条/馒头/米饭 对象
'''
'''
类的定义:
class 类名():
类体
class: 在python中使用class关键字来定义类
类名:遵循标识符的命名规则【驼峰式命名】
() 可以省略也可以写成(object)
冒号: 标识类体的开始,不能省。
'''
'''
驼峰式命名法:
大驼峰:每个单词首字母都大写,我们称之为大驼峰
一般给类来进行命名
小驼峰:第一个单词首字母小写,其他单词首字母大写
一般用户变量以及函数命名
'''
二、构造函数、析构函数及self
'''
构造函数
def __init__(self):
pass
系统定义的函数,当实例化对象的时候会自动的调用此函数。
无需自己手动调用。
此方法一般用于初始化我们的对象。
初始化对象的时候,我们需要将值绑定在self身上。【self就是我们实例化的对象】
调用类中变量以及类中的方法:
对象名.变量名
对象名.方法名(参数列表)
'''
class Person():
def __init__(self,name,sex,age,height,weight):
self.name = name
self.sex = sex
self.age = age
self.height = height
self.weight = weight
def eat(self):
print("eat",self)
print("吃饭")
def sleep(self):
print("%s睡觉"%self.name)
def say(self):
print("say hello")
if __name__ == '__main__':
per = Person("lili","girl",18,160,100)
print(per.name)
print(per.age)
print(per.height)
per.sleep()
'''
析构函数:
系统定义的函数
def __del__(self):
pass
析构函数不需要我们自己手动调用,当对象被销毁的时候会自动调用。
对象啥时候会被销毁(销毁对象):
1.当程序运行结束的时候
2.显式销毁对象(del 对象名)
'''
import time
class Person():
def __init__(self,name):
self.name = name
print("构造函数被调用啦")
def say(self):
print("say hello")
def __del__(self):
print("析构函数被调用啦。。。")
if __name__ == '__main__':
per = Person("lili")
per.say()
del per
time.sleep(10)
'''
1.self代表什么?
代表的是当前类的实例【对象自己本身】
2.self是否为关键字?
self并不是关键字可以使用别的变量名来代替,但是self是我们约定俗成的写法,
不建议使用其他的名字。
3.self可否省略?
self不能省略,并且它必须写在参数列表中第一个位置,声明的必须的要声明,传递参数的时候
不需要我们手动传参。
'''
class Person():
def __init__(self,name):
self.name = name
print("构造函数被调用啦")
def say(per,self):
print(self)
print("hello %s"%(per))
if __name__ == '__main__':
per = Person("lili")
print(Person)
print(per)
per.say("小明")
三、成员变量与类变量
'''
类变量:
定义在类中,并且定义在函数体之外的变量,我们称之为类变量【静态成员变量】
类变量通常情况不使用对象来进行调用,而是使用类名来进行调用,类变量在整个实例化
对象的过程中是公用的。
类变量的调用:
类名.变量名
注意:若使用对象来进行调用也不报错,但是一般不建议这么使用。
成员变量:
定义在类的函数中,并且要绑定在self身上的变量我们称之为成员变量,只作用于当前实例。
类变量与成员变量的区别:
1.定义位置不同
2.访问方式不同
类变量使用类名【使用对象访问不报错】
成员变量使用对象来访问 【使用类名来访问的时候会报错】
3.在内存出现的时机不同
类变量随着类的加载而出现,成员变量实例化对象的时候创建
4.调用的优先级不同,当使用对象来调用类变量与成员变量的时候,
优先选择成员变量,若成员变量不存在,则去访问类变量
5.通过对象来更改类变量的值的时候,更改的是当前对象的值【只会影响当前对象】,
通过类名来更改类变量的值的时候,更改类变量的初始值。
【会影响在类变量更改之后创建的所有对象】
'''
class Person():
name = "lili"
age = 18
height = 160
print("类变量",id(name))
def __init__(self,name="小明",age=20,sex="girl"):
self.name = name
self.age = age
self.sex = sex
if __name__ == '__main__':
per = Person()
print(per.height)
per.height = 165
print(per.height)
per2 = Person()
print(per2.height)
Person.height = 170
per3 = Person()
print(per3.height)
print(per.height)
print(per.name)
print(Person.name)
四、动态添加属性和方法
'''
正常情况下,当我们定义了一个类,我们创建类的实例之后,我们可以给该类的实例
绑定任意的属性以及方法,这就是动态数据类型语言的灵活性。
在python中,我们可以使用__slots__ 变量来限制我们动态添加的属性以及方法。
注意:__slots__ =() 的值为一个元组,元组中使用字符串进行声明可以动态添加
的属性以及函数的函数名。只作用于当前类的实例。
'''
class Person():
__slots__ = ("name","age","eat")
def __init__(self,name,age=18):
self.name = name
self.age = age
def say(self):
print("say hello")
class Student:
__slots__ = ("name","age","sex","stuid","classid","eat","study")
def __init__(self,name,age):
self.name = name
self.age = age
def sleep(self):
print("睡觉...")
def eat():
print("吃饭")
if __name__ == '__main__':
stu = Student("张三",20)
print(stu.name)
stu.eat = eat
stu.eat()
五、成员方法与类方法
'''
类中的函数/方法分为三大类:
1.成员方法
一般情况下在类中定义所有的方法我们都称之为成员方法,在成员方法中,它的第一个参数
self,声明的时候必须声明在参数列表中第一个位置,使用对象来进行调用的时候,对象会
将自己作为参数自动传递给self,不需要我们手动传递。
成员方法是给对象准备的,调用的时候必须使用对象来进行调用。
2.类方法
类方法是专门给类准备的方法,它不是绑定在对象身上,而是绑定在类身上的方法,
在声明类方法的时候我们通常情况下使用@classmethod装饰器来进行声明。
在类方法中,参数列表的第一个位置的参数是cls,代表类本身,使用类名来进行调用的
时候类会将自己作为参数自动传递进来,不需要手动传递。
调用类方法的时候建议使用类名来进行调用,若使用对象来进行调用也不报错,
但是会让别人造成误解。
3.静态方法
就是一类普通的方法,但是写在了类中。声明静态方法的时候使用@staticmethod
来进行声明,调用的时候建议使用类名来进行调用,若使用对象来进行调用也不报错。
'''
from datetime import datetime
class Person():
classname = "python1904"
@classmethod
def getclass(cls):
print("cls",cls)
print("所在班级%s"%cls.classname)
def eat(self):
print("吃饭...")
@staticmethod
def welcome():
print('''
*************************
* 欢迎来到酷我音乐播放器 *
*************************
''')
if __name__ == '__main__':
print(datetime.now())
per = Person()
per.eat()
per.getclass()
Person.getclass()
print(per)
print(Person)
'''
成员方法,类方法,静态方法使用契机:
成员方法:
当此方法中有使用到self【对象】/成员变量 的时候,必须将此方法写成成员方法。
类方法:
当此方法中没有使用到self/【成员变量】/成员方法,但是它使用到了类变量,
这时候我们必须将此函数写成类方法。
静态方法:
此方法中没有使用到成员变量,也没有使用到类变量,就是一个普通的方法,
这时候我们可以将此函数写成静态方法。
'''
六、封装、继承与多态
'''
面向对象的三大特征:
封装,继承以及多态
封装:
广义的封装:类与函数的定义就是封装的体现。
狭义的封装:类中的有些属性我们不希望外界能够直接访问,我们可以将这些属性
进行私有化只有本类持有,再给外界暴露出一个访问的接口即可。
封装的本质:属性私有化的过程
封装的优点:提高数据的复用性,保证数据的安全性
对于私有化的属性我们可以对它进行数据过滤
语法糖:
@property功能:可以将函数转为属性来进行调用。
将@property添加给getter头上,将getter变成一个属性来进行调用
与此同时,@property还会生成一个新的装饰器,这个装饰器的名字叫做
@属性.setter的装饰器,这个装饰器可以将setter方法变为属性赋值。
优点:方便调用者写出更加简洁的代码。
'''
from datetime import datetime
class Father():
def __init__(self,name,age,money):
self.name = name
self._age = age
self.__money = money
@property
def money(self):
return self.__money
@money.setter
def money(self,mon):
if mon < 10000000:
print("卡已冻结")
else:
self.__money = mon
if __name__ == '__main__':
father = Father("马爸爸",56,100000000)
print(father.name)
print(father.money)
father.money = 20000000
print(father.money)
print(father._age)
'''
被私有化的属性不能直接访问的原因是解释器在解释我们的属性的时候
将被私有化的属性解释为_类名__属性名,因此我们直接使用对象.__属性名
是访问不到的,但是若我们使用对象._类名__属性名来进行访问,是可以访问的。
但是不建议这么干,因为不同的解释器解释出来的变量名可能不相同。
'''
class Father():
def __init__(self,name,age,money):
self.name = name
self.age = age
self.__money = money
@property
def money(self):
return self.__money
@money.setter
def money(self, money):
if money < 0:
print("金额有误,存储失败!!")
else:
self.__money += money
if __name__ == '__main__':
fa = Father("马爸爸",56,10000000)
print(fa.money)
print(fa._Father__money)
fa.money = 100
print(fa.money)
'''
__foo__ 首尾双下划线,这个定义的时候使用,自己定义变量的时候也不报错。
__foo 首双下划线,私有类型,访问范围,在类本身可以访问
_foo 首单下划线,保护类型的,虽然可以直接访问,但是建议将视为私有变量来进行访问。
'''
'''
继承:
概念:如果两个或者两个以上的类具有相同的属性和方法,我们可以抽取出来一个类
将共同的部分声明到被抽取的类中。
这个被抽取出来的类我们称之为父类/超类/基类
其他类我们称之为子类/派生类
父类与子类之间的关系我们称之为继承。
注意:当一个子类没有继承其他类的时候,则它默认继承object类,换句话说,
object类是一切类的基类。
'''
'''
在python中我们的继承分为单继承与多继承。
单继承:当一个子类只有一个父类的时候我们称之为单继承。
语法:
class 类名(父类):
类体
注意:若未指明父类的时候,默认继承object类,object可以省略不写。
继承的特点:
子类可以直接使用父类未私有化的属性以及方法,但是父类不能使用子类特有的属性以及方法。
若子类拥有特殊的属性的时候,我们需要在子类重写__init__方法,在子类的init方法中
我们需要将子类拥有的所有的属性都声明(包括父类中的),还需要手动调用父类中的init方法
super().__init__(参数列表)
当子类中不存在特殊的属性【我们没有重写init方法的时候】,这时候系统会自动调用父类中的
init的方法,不需要我们自己手动调用。
父类私有化的属性与方法,我们不能直接访问,但是我们可以通过暴露出的接口
进行访问。
'''
class Animal(object):
def __init__(self,name,pinzhong,sex,color):
self.name = name
self.pinzhong = pinzhong
self.__sex = sex
self.color = color
print("父类中的构造函数被调用啦")
@property
def sex(self):
return self.__sex
@sex.setter
def sex(self,sex):
self.__sex = sex
def eat(self):
print("吃饭...")
def sleep(self):
print("睡觉")
class Cat(Animal):
def catchmouse(self):
print("捉老鼠...")
def mai(self):
print("卖萌...")
class Dog(Animal):
def kanmen(self):
print("看门...")
def faner(self):
print("拆家...")
if __name__ == '__main__':
dog = Dog("小狗","二哈","公的","白色")
dog.eat()
dog.kanmen()
dog.faner()
'''
多继承:
当一个子类有多个父类的时候我们称之为多继承。
使用多继承的时候,当两个父类中的属性不相同的时候,默认调用写在继承列表
里面最前面的位置的父类的构造函数。
若两个父类中的构造函数我们都想调用的情况下,这时候我们需要自己手动来进行
调用。
当多个父类中出现相同的方法名的时候,子类优先选择写在继承列表前面的那个。
class 类名(父类1,父类2,...):
类体
注意:()中为我们的继承列表。
练习:自定义一个栈
父亲类
特征:name sex money
行为:赚钱 打牌 抽烟 喝酒
母亲类
特征:name sex facevalue
行为:花钱 做饭 洗衣服 打麻将
'''
class Father():
def __init__(self,name,sex,money):
self.name = name
self.sex = sex
self.money = money
print("父亲的构造函数被调用啦")
def makemoney(self):
print("很会赚钱哦...")
def dapai(self):
print("会斗地主哦...")
def smoke(self):
print("会抽烟哦...")
def drink(self):
print("会喝酒哦...")
class Mother():
def __init__(self,name,sex,facevalue):
self.name = name
self.sex = sex
self.facevalue = facevalue
print("母亲的构造函数被调用啦。。。")
def spendmoney(self):
print("很会花钱哦....")
def cooking(self):
print("会煮方便面哦...")
def xiyifu(self):
print("会用洗衣机哦...")
def damajiang(self):
print("会打麻将哦...")
def dapai(self):
print("会打纸牌哦...")
class Child(Mother,Father):
def __init__(self,name,sex,money,facevalue):
Mother.__init__(self,name,sex,facevalue)
Father.__init__(self,name,sex,money)
if __name__ == '__main__':
child = Child("小明","boy",100000,99)
print(child)
print(child.name)
print(child.sex)
print(child.money)
child.cooking()
child.damajiang()
child.makemoney()
child.spendmoney()
child.dapai()
print(child.facevalue)
'''
多态:
多态本质上指一类事物的多种表现形态,它是依赖继承而存在。
动物:猫,狗,老鼠
序列:字符串,列表,元组
'''
'''
在python中不存在真正的多态,因为python是一个动态数据类型的语言。
鸭子模型:如果有一只鸟,走路像鸭子,叫声像鸭子,那么我们就称这只鸟叫鸭子。
我们不关心它的数据类型,我们只关系它的使用。
isinstance(obj,type)
功能:判断某个对象是否属于某种类型。
当这个对象属于这种类型或者属于这种类型的子类,那么返回True,否则返回False。
dir(obj)
功能:查看指定对象的所有的属性以及方法,以列表的方式返回。
'''
class Animal():
def __init__(self,name):
self.name = name
@staticmethod
def run(ani):
print("%s跑..."%ani.name)
class Cat(Animal):
pass
class Dog(Animal):
pass
class Person():
def __init__(self,name):
self.name = name
if __name__ == '__main__':
cat = Cat("Tom")
Animal.run(cat)
dog = Dog("jerry")
Animal.run(dog)
per = Person("小明")
Animal.run(per)
print(isinstance(dog,Dog))
print(isinstance(dog,Animal))
print(isinstance(per,Animal))
print(isinstance(per,(Animal,Person)))
print(dir(cat))
七、函数重写与运算符重载
'''
函数重写
当我们打印对象的时候,默认情况下显示的地址,当我们不想让其显示地址的时候
我们可以重写系统中__str__函数,当执行print()或者str()函数的时候会自动
调用此方法。因此__str__函数的返回值就是我们打印的对象的信息。
当一个类中重写了__repr__函数,而没有重写__str__函数的时候,我们可以使用
__str__ = __repr__
'''
from datetime import datetime
class Person():
def __init__(self,name,age):
self.name = name
self.age = age
def __repr__(self):
return "%s-%s"%(self.name,self.age)
__str__ = __repr__
if __name__ == '__main__':
print(datetime.now())
per = Person("lili",19)
print(per)
'''
运算符重载
'''
class Person():
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return "%s-%d"%(self.name,self.age)
def __add__(self, other):
return Person(self.name,self.age + other.age)
if __name__ == '__main__':
list1 = [1,3,4]
list2 = ["hello","good"]
print(list1+list2)
num1 = 12
num2 = 22
print(num1+num2)
per = Person("lili",18)
per2 = Person("小明",19)
per3 = Person("小李",19)
print(per+per2+per3)
'''
自定义一个字典,支持字典与字典的相加
'''
class mydict(dict):
def __add__(self, other):
dict1 = self.copy()
for k,v in other.items():
dict1[k] = v
return dict1
if __name__ == '__main__':
mydict1 = mydict({1:"hello",2:"nice"})
mydict2 = mydict({3:"good",4:"great"})
print(mydict1+mydict2)
print(mydict1)
print(mydict2)