
本文共 9282 字,大约阅读时间需要 30 分钟。
类与对象内置的一些装饰器及功能
property:通常配合隐藏属性一起使用
classmethod:绑定给类的方法,绑定给类的,无论谁来调用都会自动传入类
staticmethod:没有跟任何类和对象进行绑定,谁来调用都是一个普通函数
一切皆对象代码原理
class Student: n = 0 def __init__(self, name, age, gender): Student.n += 1 self.name = name self.age = age self.gender = gender def choose(self): # self = obj1 print("hello %s" % self.name)obj1 = Student("nana", 18, "female")print(type(obj1))print(Student)print(obj1.__dict__)obj1.choose() #对象调函数传参Student.choose(obj1) #类调函数传参内置类=内置类型print(list) #l1 = list([1, 2, 3]) print(type(l1)) # print(l1) # [1,2,3]# 操作绑定方法l1.append(4),就是往l1添加4l1.append(4) # [1, 2, 3, 4] 等同于list.append(l1,4)list.append(l1, 4) # [1, 2, 3, 4]
封装
关于类的封装的特性其实我们一直在创造类的过程中一直在使用,我们把对象中相似或相同的属性都提取出来放到一个大容器里面,而这个大容器被我们封装成了类。而我们把具象化的属性封装在一个对象里面。我们原来想传递数据需要把所有的数据都传递给别人,需要传多个值:现在我们只需要将封装了这些具体数据的对象扔给别人,需要传一个值,而其他属性都可以通过这个值拿到。除此之外,针对封装到对象或者类中的属性,我们还可以严格控制对他们的访问,分两步实现:隐藏与开放接口。
是不是我们可以获得对象里面所有的值呢?是的。但其实有些数据我们不想别人拿到或者我们不想别人随意修改那些数据,我们就需要一种方法把数据藏起来。这也就是python提供给我们的隐藏属性的方法。我们可以把属性隐藏起来,不让外部直接访问修改,我们单独对这些数据的删改查开辟接口来进行进一步的操作。(注意:同一个属性没有增加,只能删改查)
装饰器property
通常配合隐藏属性一起使用,被property装饰过的函数可以将功能属性的函数直接伪装成数据属性的函数
案例:BMI指数是用来衡量一个人的体重与身高对健康影响的一个指标,计算公式为体质指数(BMI)=体重(kg)÷身高^2(m)EX:70kg÷(1.75×1.75)=22.86
class People: def __init__(self, name, weight, height): self.name = name self.weight = weight self.height = height @property # 被property装饰过的函数可以不用再加()运行,直接将bmi伪装成数据属性的函数 def bmi(self): print(self.weight / (self.height ** 2))p1 = People("dada", 75, 1.8)p1.bmi() # 不加property的调用方式,通过对象来调bmi函数用法是一个函数属性的函数p1.bmi # 加property实现了将功能属性像数据属性一样调用的效果
property是提供给不需要传参的功能属性添加装饰器的方式,直接将功能属性的使用方法转换成了数据属性的使用方法。使用property有效地保证了属性访问的一致性。另外property还提供了设置和删除属性的功能。
class People: def __init__(self, name): self.__name = name # 将属性隐藏起来 @property def name(self): # 查看名字的接口 return self.__name @name.setter # 函数名.setter装饰过的函数,可以直接修改形参x.可以直接写成p1.name="lala") def name(self, value): # 修改名字的接口 if type(value) is not str: # 在设定值之前进行类型检查 raise Exception("名字必须是字符串类型") # raise Exception主动抛出异常 self.__name = value # 通过类型检查后,将值value存放到真实的位置self.__name @name.deleter # 添加deleter装饰器,不允许删除 def name(self): # 删除名字的接口 print("不允许删除")p1 = People("nana")print(p1.name) # 调用查看名字的接口p1.name = "lala" # 调用修改名字的接口print(p1.name)del p1.name # 调用删除接口
写法二:class People: def __init__(self, name): self.__name = name def get_name(self): return self.__name def set_name(self, value): if type(value) is not str: raise Exception("名字必须是字符串类型") # raise Exception主动抛出异常 self.__name = value def del_name(self): print("不允许删除") name = property(get_name, set_name, del_name)p1 = People("nana") # 调用查看名字的接口print(p1.name)p1.name = "lala" # 调用修改名字的接口print(p1.name)del p1.name # 调用删除接口
绑定方法与非绑定方法
类中定义的函数分为两大类:绑定方法和非绑定方法。其中绑定方法又可以分为绑定到对象的绑定方法和绑定给类的类方法。
绑定方法我们在介绍类和对象关系的时候提到过,类的功能属性只存在于类的名称空间中,对象想要调用类中的功能属性时就是把一个链接放在对象的名称空间中,这个链接可以直接指向类中功能的内存地址,类中的功能是相当于绑定方法拿给对象用的。绑定方法给谁就是由谁来用,谁来调用就把自己当作第一个参数传入。
绑定方法
特点:绑定给谁就应该由谁来调用,谁来调用就会将自己当作第一个参数传入非绑定方法
特点:不与类和对象绑定,意味着谁都可以来调用,但无论谁来调用就是一个普通函数,没有自动传参的效果# 类的绑定方法演示# 配置文件settings.py的内容HOST = "127.0.0.1"PORT = 3306# 类方法的应用类内部传参,无法去全局名称空间里面找值import settingsclass Mysql: def __init__(self, host, port): self.host = host # Mysql.host = "127.0.0.1" self.port = port # Mysql.port = 3306 @classmethod def from_conf(cls): # 从配置文件中读取配置进行初始化 return cls(settings.HOST, settings.PORT) # 丢出来的返回值 类来调用,传入参数HOST,PORTMysql.from_conf # 绑定到类的方法#>conn = Mysql.from_conf() # 调用类方法,自动将类Mysql当作第一个参数传给cls
类的非绑定方法演示import uuidclass Mysql: def __init__(self, host, port): self.id = self.create_id() # Mysql来调用就是一个普通函数 self.host = host self.port = port @staticmethod def create_id(): return uuid.uuid1()conn = Mysql("127.0.0.1", 3306)# print(conn.__dict__) # { 'id': UUID('b05b9dd3-7b2f-11eb-9440-98fa9b6b22f2'), 'host': '127.0.0.1', 'port': 3306}# 类或者对象来调create_id都只是普通函数,而非绑定到谁的绑定方法print(Mysql.create_id) #print(conn.create_id) #
class People: def __init__(self, name): self.name = name # 但凡在类中定义一个函数,默认就是绑定给对象的,应该由对象来调用, # 会将对象当作第一个参数自动传入 def tell(self): print(self.name) # 类中定义的函数被classmethod装饰过,就绑定给类,应该由类来调用 # 类调用会将类本身当作第一个参数自动传入 @classmethod # classmethod会将函数f1绑定给类People def f1(cls): # cls=People print(cls) # 类中定义的函数被staticmethod装饰过,就成了一个非绑定的方法即一个普通函数,谁都可以调用 # 但无论谁来调用就是一个普通函数,没有自动传参的效果 @staticmethod # staticmethod会将函数f2变成非绑定方法的普通函数 def f2(x, y): print(x, y)p1 = People("nana") # 实例化得到一个对象p1绑定给对象的函数p1.tell() 绑定给类的函数print(People.f1) # 打印结果显示为一个绑定方法>People.f1() # 类名称 非绑定方法的函数People.f2(1, 2) # 类来调用 1,2p1.f2(3, 4) # 函数来调用 3,4
绑定方法与非绑定方法装饰器应用
配置文件settings.py的内容name = "dada"age = 19gender = "male"应用方法import settingsimport uuidclass People: def __init__(self, name, age, gender): self.id = self.creat_id() self.name = name self.age = age self.gender = gender def tell_info(self): # 默认是绑定给对象的函数 print(self.name, self.age, self.gender) @classmethod # 绑定给类的方法应该由类去调用,如果对象去调用还是会自动传入类的参数 def from_conf(cls): # 导入模块settings,从配置文件读取文件完成实例化 return cls(settings.name, settings.age, settings.gender) @staticmethod # 非绑定方法 def creat_id(): return uuid.uuid1()p1 = People("lala", 18, "male") # 实例化得到一个对象p1print(p2.__dict__) # 字典{ key : value}形式p2 = People.from_conf() # 类调用函数绑定方法实例化得到一个对象p2print(p2.__dict__) # 把settings模块的变量值传给了p2,生成了一个字典print(p1.creat_id()) # 普通函数print(People.creat_id()) # 普通函数
继承
对象之间相似属性可以提取出来放在类里面
我们也可以把类和类的相似数据属性和功能属性拿出来,放在父类里面选课系统# 代码演示:提取前class Student: def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def tell_info(self): print('{}{}{}'.format(self.name,age,gender)) def choose_course(self): print('{}正在选课'.format(self.name)) class Teacher: def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def tell_info(self): print('{}{}{}'.format(self.name,age,gender)) def grade(self): print('{}正在评分'.format(self.name))
# 代码演示:提取后class People: def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def tell_info(self): print('{}{}{}'.format(self.name,age,gender)) class Student(People): def choose_course(self): print('{}正在选课'.format(self.name)) class Teacher(People): def grade(self): print('{}正在评分'.format(self.name))
把Student类和Teacher类共有的属性提取出来,创建了一个新类,这个新类也叫Student和Teacher的父类/超类/基类,而Student和Teacher称为子类。继承的优点就是,子类可以遗传父类所有的属性and代码简介。缺点是强耦合。可以通过代码print(类.bases)来查看这个类的所有父类。
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,新建的类可以称为子类或派生类,父类又可以称为基类或者超类。
在python中支持多继承 class Parent1: # 定义父类 passclass Parent2: # 定义父类 passclass Sub1(Parent1): # 单继承 passclass Sub2(Parent1, Parent2): # 多继承 pass# 可以通过代码print(类.__bases__)来查看这个类的所有父类print(Sub1.__bases__) # (,)print(Sub2.__bases__) # ( , )print(Parent1.__bases__) # ( ,)print(Parent2.__bases__) # ( ,)
新式类:在python3中,但凡是继承了object的子类以及该类的子子孙孙类都是新式类。(python3的所有类都是新式类)
经典类:一般python2中的类都是经典类。如果在python2中把经典类改成新式类,需要在类名称的后面加(object)
先抽象,再继承
要找出类与类之间的继承关系,需要先抽象,再继承。抽象即总结相似之处,总结对象之间的相似之处得到类,总结类与类之间的相似之处就可以得到父类,如下图所示

基于抽象的结果,我们就找到了继承关系

class Teacher: school='清华大学' def __init__(self,name,sex,age): self.name=name self.sex=sex self.age=age def teach(self): print('%s is teaching' %self.name)
类Teacher与Student之间存在重复的代码,老师与学生都是人类,所以我们可以得出如下继承关系,实现代码重用
class People: school='清华大学' def __init__(self,name,sex,age): self.name=name self.sex=sex self.age=ageclass Student(People): def choose(self): print('%s is choosing a course' % self.name)class Teacher(People): def teach(self): print('%s is teaching' %self.name)
Teacher类内并没有定义__init__方法,但是会从父类中找到__init__,因而仍然可以正常实例化,如下
teacher1=Teacher("nana","female","18") # Teacher调用父类People的init方法实例化得到teacher1对象print(teacher1.__dict__)# { 'name': 'nana', 'sex': 'female', 'age': '18'}teacher1.teach() # 对象teacher1直接调用函数teach功能就可以
继承背景下的属性查找
继承背景下的属性查找的顺序:先从对象里面找,如果对象里面没有,再从类里面查找,如果类里面也没有,再从父类里面找
class Bar: # Bar的父类不写默认是object y = 666class Foo(Bar): def __init__(self, x): self.x = xobj = Foo(111) # 调用Foo传入参数111生成对象objprint(obj.__dict__) # { "x": 111}print(obj.x) # 111print(obj.y) # 666
示例一:class Bar: # 定义一个父类 def f1(self): print("Bar.f1") def f2(self): print("Bar.f2") self.f1() class Foo(Bar): # 定义一个子类 def f1(self): print("Foo.f1")obj = Foo() # 类Foo实例化得到一个对象obj obj.f2() # 对象obj调用父类Bar里面的函数f2 "Bar.f2" ---> "Foo.f1"示例二:class Bar: # 定义一个父类 def __f1(self): "_Bar__f1" print("Bar.f1") def f2(self): print("Bar.f2") self.__f1() "self._Bar__f1"class Foo(Bar): # 定义一个子类 def __f1(self): "_Foo__f1" print("Foo.f1")obj = Foo() # 调用类Foo实例化得到对象objobj.f2() # "Bar.f2" ---> "Bar.f1"# 对象里面的函数名变化了,找 self.__f1(),其实是在找self._Bar__f1
发表评论
最新留言
关于作者
