一、面向对象基础
1.动态绑定属性和限制绑定
#1.动态绑定属性 class Person(): def show(self): print("姓名:%s,成绩:%d,身高:%.2f" % (self.name,self.score,self.height)) per1 = Person() #注意:被绑定的属性只能被当前对象持有 per1.name = "张三" per1.score = 99 per1.height = 173.0 per1.age = 18 per1.show() per2 = Person() #print(per2.name) #AttributeError属性错误: 'Person' object has no attribute 'name' #2.限制绑定 class Person1(): #注意:使用__slots__限制需要绑定的属性,语法:__slots__ = ("属性1","属性2"。。。。) #魔术方法:__xxxx__,一般都是系统的函数或者变量,自定义的函数或者变量尽量不要使用__xxx__ __slots__ = ("name","score","height") def show(self): print("姓名:%s,成绩:%d,身高:%.2f" % (self.name,self.score,self.height)) p1 = Person1() p1.name = "李四" p1.score = 77 p1.height = 183.3 #p1.age = 18 #AttributeError: 'Person1' object has no attribute 'age' p1.show()
2.内存中的对象
class Person(): #类属性 name = "abc" def show(self): print("showing") #1.注意1:一个普通的类,理论上可以创建无数个对象 p1 = Person() p2 = Person() print(id(p1),id(p2)) #4378443336 4378443392 p1.show() p2.show() print(p1.name,p2.name) #2.注意2:不同的对象直接访问【对象.变量】定义在类中的变量,访问的是同一块内存空间 print(p1.name is p2.name) print(id(p1.name) == id(p2.name)) #4378171632 4378171632 #3.注意3:不同的对象通过动态绑定的方式绑定了相同成员变量,指向不同的内存空间 #对象.变量 = 值 #实例属性 p1.age = 18 p2.age = 18 print(p1.age is p2.age) #True print(id(p1.age) == id(p2.age)) ##True p1.age = 20 print(p2.age) print(p1.age is p2.age) #False print(id(p1.age) == id(p2.age)) #False """ 总结: a.变量中存储的是数据的地址 b.通过动态绑定的方式给对象绑定属性,不同对象的属性指向不同的内存空间, 但是,该内存空间中如果存储的数据相同,那么通过is或者id比较结果为True """
3.构造函数
使用直接赋值【动态绑定属性】方式创建对象,可以使用,但是代码比较繁杂,一般情况下,很多类倾向于将对象创建为有初始状态的,在类中可以定义一个函数,名称为__init__,该特殊的函数被称为构造函数,主要用于创建对象并将对象的数据做出初始化 强调:构造函数包括__new__和__init__ 构造函数,也被称为构造器,指的是当创建对象的时候,被自动调用的函数 语法: def __init__(self): 函数体
3.1__init__
#1.直接赋值 #缺点:代码比较繁杂,后期不利于维护,可读性低 """ class Dog(): pass d1 = Dog() d1.name = "小白" d1.kind = "泰迪" d1.color = "棕色" d2 = Dog() d2.name = "大黄" d2.kind = "土狗" d2.color = "浅黄色" d3 = Dog() d3.name = "旺财" d3.kind = "二哈" d3.color = "白色" """ #2.__init__:构造函数 """ a.可以用来创建对象,并可以将对象创建为初始数据的 b.创建对象的过程中会被自动调用 """ # a.无参 class Dog(): def __init__(self): print("init被调用了") def show(self): print("showing") d1 = Dog() d1.show() # d1.__init__() """ 注意: a.当创建对象的时候,会自动调用__init__函数,当没有显式的书写__init__时, 系统默认提供了一个无参的__init__ b.d1.__init__()可以手动调用,但是一般不这样使用 """ print("=" * 30) #b.有参 class Dog(): def __init__(self,name,kind,color): #当创建对象的时候,当前被创建的对象会自动传参,传给self self.name = name self.kind = kind self.color = color #return 399 #TypeError: __init__() should return None, not 'int' def show(self): print("show~~~bbbb",self.name,self.kind,self.color) d1 = Dog("小白","泰迪","棕色") d2 = Dog("大黄","土狗","浅黄色") print(d1.name) print(d2.kind) d3 = Dog("旺财","二哈","白色") d1.show() d2.show() d2.show() d2.show() #说明:__init__和__slots__是两回事,在__init__中定义了变量,如果没有限制绑定,还是可以动态绑定 d1.age = 5 print(d1.age) """ 注意: a.__init__和普通函数的用法相同,仍然需要注意参数的匹配 b.__init__在创建有初始化数据的对象时,可以简化代码 【面试题】简述构造函数__init__和普通成员函数之间的区别 a.构造函数仍然是一个成员函数 b.构造函数的函数名是固定的,但是普通成员函数的函数名可以自定义 c.构造函数不需要手动调用,系统自动调用,但是普通成员函数必须手动调用 d.一般情况下,对于一个对象而言,构造函数只会被调用一次,但是,普通成员函数可以被调用无数次 """
3.2__new__
""" __init__可以定义一个对象的初始化操作,但是,__init__并不是第一个被自动调用的函数, 实际上,还有一个__new__,两个函数构成了构造函数 """ #1.只定义__init__ class Check1(): def __init__(self): print("init被调用了") c1 = Check1() print(type(c1)) #<class '__main__.Check1'> print("=" * 30) #2.定义__init__和__new__ class Check2(): #a.init:self表示当前的实例【对象】 def __init__(self): print("init被调用了~~~~",self) # b.new:cls表示当前类 # def __new__(cls, *args, **kwargs): # print("new被调用了", cls) # # return super(Check2,cls).__new__(cls, *args, **kwargs) c2 = Check2() print(c2) print(type(c2)) """ 一般情况下,使用init较多,直接创建有初始化数据的对象 但是,构造函数的调用先调用new,然后再调用init """
4.析构函数
构造函数:当对象创建的时候自动调用的函数 和构造函数相反,当对象被销毁的时候自动调用的而寒暑,称为析构函数,函数名为__del__
class Person(): def __init__(self): print("init被调用了") def __del__(self): print("del被调用了") #1.将对象定义成全局变量,程序执行完毕,对象自动被销毁 # print("start") # p1 = Person() # print("end") """ start init被调用了 end del被调用了 """ #2.将对象定义成局部变量 # def show(): # p2 = Person() # print("start") # show() # print("end") """ start init被调用了 del被调用了 end """ #3.程序未执行完毕,对象被手动销毁 print("start") p3 = Person() print("end") del p3 print("over") """ start init被调用了 end del被调用了 over """ #总结:del函数的作用:一般用来进行一些清理工作,比如:操作文件之后关闭文件,操作数据库之后关闭数据库等
5.练习
""" 富二代类: 特征:姓名 行为:开车,炫耀 """ class Richman(): def __init__(self,name): self.name = name #car表示汽车的对象,实参必须是一个汽车的对象 def drive(self,car): print(self.name + "开着豪车" + car.brand) #per表示女友的对象 def show(self,per,car): print(self.name + "向" + per.name + "炫耀豪车" + car.brand + ",你看你这颜色" + car.color + "啧啧啧~~~~")
""" 汽车类: 特征:品牌,颜色 行为:奔跑 """ class Car(): def __init__(self,brand,color): self.brand = brand self.color = color def run(self): print(self.brand + "running")
class Gf(): def __init__(self,name): self.name = name
#测试文件 """ 注意: a.在创建包的时候【导入模块】,注意命名:尽量不要出现中文和特殊符号 """ """ 需求:富二代王思聪开着新车兰博基尼,向他的新女友炫耀 富二代类: 特征:姓名 行为:开车,炫耀 汽车类: 特征:品牌,颜色 行为:奔跑 女友类: 特征:姓名 """ from practice01.richman import Richman from practice01.car import Car from practice01.gf import Gf #1.创建对象 wang = Richman("王思聪") car = Car("五菱宏光","骚粉色") gf = Gf("凤姐") #2.让富二代执行自己的行为 wang.drive(car) wang.show(gf,car)
二、封装
1.概念
广义的封装:函数的定义和类的提取,都是封装的体现
狭义的封装:在面向对象编程中,一个类的某些属性,在使用的过程中,如果不希望被外界【直接】访问,就可以将该属性封装【将不希望被外界直接访问的属性私有化private,该属性只能被当前类持有,此时可以给外界暴露一个访问的函数即可】
封装的本质:就是属性私有化的过程
封装的好处:提高了数据的安全性,提高了数据的复用性
举例:插排,不需要关心属性在类的内部被做了什么样的操作,只需要关心可以将值传进去,也可以将值获取出来
2.属性私有化
#1.属性未被私有化 class Person1(): def __init__(self,name,age): self.name = name self.age = age def show(self): print(self.name,self.age) p1 = Person1("张三",10) p1.show() #通过对象直接访问属性,并且给属性重新赋值 p1.name = "jack" p1.age = 20 p1.show() print("=" * 30) #2.属性私有化 #语法:在属性的前面添加两个下划线 class Person2(): def __init__(self,name,age): #私有属性 self.__name = name #公开属性 self.age = age def show(self): print(self.__name,self.age) p2 = Person2("张三",10) p2.show() #注意:私有化的属性只能在类的内部被直接访问,在外界不能直接访问 #print(p2.__name) #AttributeError: 'Person2' object has no attribute '__name' #动态绑定属性 # p2.name = "jack" # p2.show() # p2.__name = "hello" # p2.show() #工作原理:一般情况下,私有化的属性被Python解释器解释称了 _类名__属性名, # 但是不希望通过这种方式访问私有化属性,因为Python是跨平台的,不同的解释器可能会解释成不同的格式 #print(p2._Person2__name) print("=" * 30) #3.提供暴露给外界的函数 class Person3(): def __init__(self,name,age): self.__name = name self.__age = age def show(self): print(self.__name,self.__age) #传值,修改私有属性的值,设置参数 def setName(self,name): self.__name = name #获取值,将私有属性的值返回,设置返回值 def getName(self): return self.__name def setAge(self,age): if age < 0: age = -age self.__age = age def getAge(self): return self.__age p3 = Person3("张三",10) p3.show() #在类的外面访问私有化属性:传值或者获取值 p3.setName("jack") p3.show() n = p3.getName() print(n) p3.setAge(-37) print(p3.getAge()) print("=" * 30) #4.@property class Person4(): def __init__(self,name,age): self.__name = name self.__age = age def show(self): print(self.__name,self.__age) #@property, 获取值,将私有属性的值返回,设置返回值 #注意:@property装饰器的作用将函数转化为属性使用 @property def name(self): print("name~~~property") return self.__name #@xxx.setter,传值,修改私有属性的值,设置参数 #注意:xxx表示被@property修饰的函数的函数名 @name.setter def name(self,name): print("name~~~~~~~~~setter") self.__name = name @property def age(self): return self.__age @age.setter def age(self,age): if age < 0: age = -age self.__age = age p4 = Person4("tom",22) p4.show() #print(p4.name()) #TypeError: 'str' object is not callable print(p4.name) #对象.函数名 相当于调用被@property修饰的函数 p4.name = "bob" #对象.函数名 = 值 相当于调用被@xxx.setter修饰的函数 print(p4.name)