7. python - 面向对象:类、对象、属性、方法

文章目录

  • 7. 面向对象:类、对象、属性、方法
    • 7.1 面向对象概念
    • 7.2 类
    • 7.3 对象
    • 7.4 属性
      • 7.4.1 类属性分类
      • 7.4.2 类属性(类变量)
      • 7.4.3 实例变量
      • 7.4.4 局部变量
    • 7.5 方法
      • 7.5.1 实例方法
      • 7.5.2 类方法
      • 7.5.3 静态方法
      • 7.5.4 特殊方法
        • 7.5.4.1 构造方法__init__()
        • 7.5.4.2 创建类实例的静态方法__new__()
        • 7.5.4.3 返回实例化对象信息__repr__()和__str__()
        • 7.5.4.4 销毁对象__del__()
        • 7.5.4.5 列出对象的所有属性__dir__()
        • 7.5.4.6 查看对象内部所有属性名和属性值组成的字典__dict__
    • 7.6 类属性使用高级方法
      • 7.6.1 property()函数
      • 7.6.2 装饰器
      • 7.6.3 描述符

7. 面向对象:类、对象、属性、方法

7.1 面向对象概念

  • 面向对象编程(Object-oriented Programming,简称 OOP),是一种封装代码的方法
  • 面向对象:将模拟真实世界里的事物(对象 )和描述其特征(属性)的数据和函数代码块(方法)封装到一起(类)
    • :可以理解是一个模板,通过它可以创建出无数个具体实例
    • 对象:类并不能直接使用,通过类创建出的实例(对象)才能使用
    • 属性:类中的所有变量称为属性
    • 方法:类中的所有函数通常称为方法

7.2 类

  • 创建类
    • class 关键字定义类
    • 属性 : 包含在类中的变量
    • 方法 :包含类中的函数
    • 类说明 :在类头之后用字符串可以添加类定义说明
class 类名:
	'''
	类说明
	'''
    类属性
    类方法

7.3 对象

  • 创建对象
    • 要想使用一个类,必须创建该类的对象
    • 类的实例化 :创建类对象的过程
    • 语法格式:
对象名 = 类名(参数,...
  • 类对象使用
    • 使用类变量 :类对象名.变量名
    • 使用类方法 :对象名.方法名(参数)
  • 类对象添加删除新的属性
    • 添加属性 :类对象名.新变量名 = 新变量值
    • 删除属性 : del 类对象名.变量名
  • 类对象添加新的方法
def 方法名(self , [ parm, ...]:
	pass
from types import MethodType # 导入模块
类对象名.方法名 = MethodType(方法名, 对象名) # 将方法和对象绑定

7.4 属性

7.4.1 类属性分类

  • 在类体中,根据变量定义的位置不同,以及定义的方式不同,类属性又可细分为以下 3 种类型
    • 类体中、所有函数之外:此范围定义的变量,称为类属性类变量
    • 类体中,所有函数内部:以 self.变量名 的方式定义的变量,称为实例属性实例变量
    • 类体中,所有函数内部:以 变量名=变量值 的方式定义的变量,称为局部变量

7.4.2 类属性(类变量)

  • 类变量的特点:
    • 所有类的实例化对象都同时共享类变量,也就是说,类变量在所有实例化对象中是作为公用资源存在的
    • 类变量为所有实例化对象共有,可以通过类名修改类变量的值,并且修改后,会影响所有的实例化的对象
    • 类对象无法修改类变量的值,通过类对象对类变量赋值,只会修改自己对象中的变量值
  • 示例:
class person :
    name = "QWQ" # 类变量 :name
    age = 18     # 类变量 :age
    def info(self) : 
        print("%s age is %d"%( self.name, self.age))

jone = person()  
jone.info()  # 打印 :QWQ age is 18
jack = person()
jack.info()  # 打印 :QWQ age is 18

person.name = "WXQ"   # 通过类名改变类变量值, 改变了模子里面的值
person.age = 20       
jone.info()  # 打印 :WXQ age is 20 
jack.info()  # 打印 :WXQ age is 20

jone.name = "LNL" # 通过类对象改变类变量值
jone.age = 19
jone.info()  # 打印 :LNL age is 19 , 对象中类变量发生改变
jack.info()  # 打印 :WXQ age is 20 , 类中类变量值不会发生变化

7.4.3 实例变量

  • 实例变量指的是在任意类方法内部,以 self.变量名 的方式定义的变量,其特点:
    • 只作用于调用方法的对象
    • 实例变量只能通过对象名访问,无法通过类名访问
    • 通过类对象是无法修改类变量的值,更不会影响类的其它实例化对象
  • 示例 :
class person :
    def __init__(self) -> None:
        self.name = "QWQ" # 实例变量
        self.age = 18     # 实例变量
    def info(self) : 
        self.tall = 178   # 实例变量
        
jone = person()  
print("%s age is %d"%(jone.name, jone.age))  #打印 :QWQ age is 18,通过对象访问实例变量
# print(person.name) 无法通过类名访问实例变量
# print(jone.tall)  在info方法没用调用前,还不存在tall变量,无法调用
jone.info() # 调用info方法,创建了tall实例变量
print(jone.tall) # 打印 :178

jone.name = "WXQ" # 通过对象修改实例变量
jone.age = 20
jone.tall = 172
print("%s age is %d, tall is %d"%(jone.name, jone.age,jone.tall)) 
jack = person()
jack.info()  
print("%s age is %d, tall is %d"%(jack.name, jack.age,jack.tall)) # 类中实例变量值没有改变

结果:

QWQ age is 18
178
WXQ age is 20, tall is 172
QWQ age is 18, tall is 178

7.4.4 局部变量

  • 局部变量直接以 变量名=值 的方式进行定义
  • 定义局部变量是为了所在类方法功能的实现,局部变量只能用于所在函数中,函数执行完成后,局部变量也会被销毁
class Calculate :
    def count(self,money):
        sale = 0.8*money # sale 为局部变量
        print("优惠后的价格为:",sale)
clang = Calculate()
clang.count(100)

7.5 方法

  • 方法可分为类方法实例方法静态方法
    • @classmethod 修饰的方法为类方法
    • @staticmethod 修饰的方法为静态方法
    • 不用任何修饰的方法为实例方法

7.5.1 实例方法

  • 类中定义的方法默认都是实例方法,不仅如此,类的构造方法等特殊的魔术方法,理论上也属于实例方法
  • 实例方法:最少也要包含一个 self 参数,用于绑定调用此方法的实例对象(Python 会自动完成绑定)
  • 实例方法通常会用类对象直接调用
class class_name():
	def __init__(self) :
		pass
	def func(self):  # 实例方法
		pass
object_name = class_name() # 定义对象
object_name.func() # 调用实例方法
class_name.func(object_name) # 类名调用实例方法,需手动给 self 参数传值

7.5.2 类方法

  • 类方法和实例方法相似,也要包含一个参数,通常将其命名为 cls
  • Python 会自动将类本身绑定给 cls 参数
  • 类方法需要使用@classmethod修饰符进行修饰
class class_name():classmethod
	def func(cls):  # 类方法
		pass
		
class_name.func() # 使用类名直接调用类方法
object_name = class_name() # 使用类对象调用类方法
object_name.func() 

7.5.3 静态方法

  • 静态方法,本质上就是函数,区别在于,静态方法定义在类这个空间(类命名空间)中,而函数则定义在程序所在的空间(全局命名空间)中
  • 静态方法没有类或对象的绑定,因此静态方法中无法调用任何类属性和类方法
  • 使用@staticmethod修饰
class person:
    @staticmethod
    def info(name,add): # 静态方法
        print(name,add)

peron.info("QWQ",18) # 使用类名直接调用静态方法
jack = person()      # 使用类对象调用静态方法
jack.info("jack",24)

7.5.4 特殊方法

7.5.4.1 构造方法__init__()

  • 构造方法 : 用于创建对象时初始化赋值
  • 第一个参数必须存在且必须为 self ,其它参数个数无限
class class_name :
	def __init__(slef, parm1, parm2,...):
		# self :新建的对象
		slef.parm1 = parm1
		slef.parm2 = parm2

7.5.4.2 创建类实例的静态方法__new__()

  • 概念理解
    • __new__() 创建类的实例化对象时调用,相对于给实例化的对象申请一片内存,如果创建的类中没有写__new__(),实际上执行时会调用object类中的__new__() 申请返回内存地址,也就是说我们使用的该方法实际是一种重写父类方法
  • 特性
    • 触发机制 : __new__() 方法是在类准备将自身实例化时调用执行
    • __new__() 方法始终都是类的静态方法,即使没有被加上静态方法装饰器
    • 需要用 return 返回该类的一个实例对象地址
  • 作用
    • 用于继承一些 不可变的class 时(比如int, str, tuple), 提供一个自定义这些类的实例化过程的途径
"""
示例1 :
"""
class int_define():  
    def __init__(self, value) :
        self.value = abs(value)
class PositiveInterger_define(int_define):
    def __init__(self, value):
        super().__init__(value) # 通过super, 调用父类的构造方法
    def __repr__(self) -> str:
        return str(self.value)
i = PositiveInterger_define(-3)
print(i) # 打印结果为3
"""
示例2:
"""
class PositiveInterger(int) :
    def __init__(self, value) :
        super().__init__(value) # 同样试图用super()调用父类int的构造方法
    def __repr__(self) -> str:
        return str(self.value)   
i = PositiveInterger(-3) # 程序报错,无法执行
"""
示例3:
"""
class PositiveInterger1(int) :
    def __new__(cls, value) :  # 通过__new__()才能达到想要的结果
        return super().__new__(cls, abs(value))  
i = PositiveInterger1(-3)
print(i)

7.5.4.3 返回实例化对象信息__repr__()和__str__()

  • __repr__() : 类的实例化对象用来做 自我介绍 的方法,默认情况下,它会返回当前对象的 类名+object at+内存地址 ,而如果对该方法进行重写,可以为其制作自定义的自我描述信息
  • __str__() : 在__str__方法中添加 return+ 打印对象看到内容,实现打印对象名时自动触发去调用出__str__里面的内容
  • 区别 :对于自己编写的类来说,两者基本没有区别
# 示例1 :
class person():
    def __init__(self, name, age):
        self.name = name
        self.age = age
QWQ = person("QWQ", 18)
print(QWQ) # 打印 :<__main__.person object at 0x000001F3E8864250>

# 示例2 :
class person():
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __repr__(self): # 对父类object进行重写
        return "{} age is {}".format(self.name, self.age)
QWQ = person("QWQ", 18)
print(QWQ)  # 打印 : QWQ age is 18

# 示例3 :
class person():
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __str__(self): # 对父类object进行重写
        return "{} age is {}".format(self.name, self.age)
QWQ = person("QWQ", 18)
print(QWQ)  # 打印 : QWQ age is 18

7.5.4.4 销毁对象__del__()

  • 垃圾回收(简称GC)实现函数:如果之前创建的类实例化对象后续不再使用,通过__del__()函数在适当位置手动将其销毁,释放其占用的内存空间
  • 语法格式
class class_name:
    def __init__(self):
        pass
    def __del__(self):
        pass
c = class_name()  # 创建对象
del c             # 销毁对象
  • 特点
    • python的主动回收机制也是通过该方法实现的,因此自己添加的回收方法是重写父类的 __del__() 方法,因此必须显式调用父类的 __del__() 方法,这样才能保证在回收子类对象时,其占用的资源能被彻底释放
    • Python 采用自动引用计数(简称 ARC)的方式实现垃圾回收机制。其核心思想是:每个 Python 对象都会配置一个计数器,初始 Python 实例对象的计数器值都为 0,如果有变量引用该实例对象,其计数器的值会加 1,依次类推;反之,每当一个变量取消对该实例对象的引用,计数器会减 1。当对象的的计数器值为 0,则表明没有变量引用对象,即证明程序不再需要它,此时 才会自动调用 __del__() 方法将其回收。

7.5.4.5 列出对象的所有属性__dir__()

  • 和内置 dir() 函数一样,__dir__()方法用于查看对象拥有的所有的属性名和方法名,返回一个包含有所有属性名和方法名的有序列表
  • dir() 函数的内部实现,其实是在调用参数对象 __dir__() 方法的基础上,对该方法返回的属性名和方法名做了排序
  • 使用 __dir__() 方法和 dir() 函数输出的数据是相同,仅仅顺序不同
  • 语法格式 :对象名.__dir__()

7.5.4.6 查看对象内部所有属性名和属性值组成的字典__dict__

  • __dict__ 属性 : 用于查看类中包含哪些属性
  • 特点
    • 用类名直接调用 __dict__,会输出该由类中所有类属性组成的字典
    • 用类的实例对象调用 __dict__,会输出由类中所有实例属性组成的字典
    • 具有继承关系的父类和子类,通过类名查看类属性,仅查看自己包含的属性,父类、子类互不影响
    • 借助由类实例对象调用 __dict__ 属性获取的字典,可以使用字典的方式对其中实例属性的值进行修改
  • 语法格式 :
    • 类名调用 : 类名.__dict__
    • 实例对象调用 : 对象名.__dict__

7.6 类属性使用高级方法

  • 通过类对象.属性的方式访问类中定义的属性的方法,破坏了类的封装原则。正常情况下,类包含的属性应该是隐藏的,只允许通过类提供的方法来间接实现对类属性的访问和操作,因此python提供了许多类属性操作的高级方法
  • 破坏封装性的用法
class person :
	def __init__(self,name):
		self.name = name

# 直接用 对象名.属性 进行操作,破坏了类的封装性
QWQ = person("QWQ") 
print(QWQ.name)    # 调用属性
QWQ.name = 'LNL'   # 修改属性
  • 正确做法是类提供的方法来间接实现对类属性的访问和操作
class person :
	def __init__(self,name):
		self.name = name
	def getname(self) :  # getter 方法
		return self.name
	def setname(self, name) :  # setter 方法
		self.name = name
	def delname(self) :   # del 方法
		self.name = "xxx"

QWQ = person("QWQ") 
print(QWQ.getname())    # 调用属性
QWQ.setname('LNL')      # 修改属性
QWQ.delname()

7.6.1 property()函数

  • 在不破坏类封装原则的基础上,又希望通过 类对象.方法(参数) 的方式来操作属性,python提供了 property() 函数
  • 语法 :属性名 = property(fget=None, fset=None, fdel=None, doc=None)
    • fget 参数用于指定获取该属性值的类方法
    • fset 参数用于指定设置该属性值的方法
    • fdel 参数用于指定删除该属性值的方法
    • doc 是一个文档字符串,用于说明此函数的作用
  • 此时该属性必须设置为私有属性,即使用 __属性名(前面有 2 个下划线)
class person:
    def __init__(self,name): #构造函数
        self.__name = name
    def setname(self,name) : #设置 name 属性值的函数
        self.__name = name
    def getname(self): #访问nema属性值的函数
        return self.__name
    def delname(self) : #删除name属性值的函数
        self.__name = "xxx"
    name = property(getname, setname, delname, '姓名操作') #为name属性配置property()函数

print(person.name.__doc__) # 调取说明文档的 2 种方式
help(person.name)
QWQ = person("QWQ") 
print(QWQ.name)   # 调用 getname() 方法
QWQ.name = "LNL"  # 调用 setname() 方法
del QWQ.name      # 调用 delname() 方法

7.6.2 装饰器

  • 既要保护类的封装特性,又要让开发者可以使用 对象.属性 的方式操作操作类属性,除了使用 property() 函数,Python 还提供了 @property 装饰器
  • @property 装饰器,可以直接通过方法名来访问方法,不需要在方法名后添加一对()小括号
  • @property 的语法格式如下:
@property
def 变量名(self)
    代码块
  • 修改属性的值需要用到 setter 装饰器 :
@变量名.setter
def 变量名(self, value):
    代码块

当构造函数__init__()中,属性没有加下划线,此时会先执行setter装饰器的设置函数,利用该特性可以在setter装饰器函数中添加判断条件

  • 删除指定属性的值用 deleter 装饰器 :
@变量名.deleter
def 变量名(self):
    代码块
  • 使用实例 :
class Rect:
    def __init__(self,area):
        self.__area = area
    @property
    def area(self):
        return self.__area
    @area.setter
    def area(self, value):
        self.__area = value
    @area.deleter
    def area(self):
        self.__area = 0

rect = Rect(30) 
print("矩形的面积是:",rect.area) #直接通过方法名来访问 area 方法
rect.area = 90
print("修改后的面积:",rect.area)
del rect.area
print("删除后的area值为:",rect.area)

7.6.3 描述符

  • 描述符就是一个类,只不过它定义了另一个类中属性的访问方式, 换句话说,一个类可以将属性管理全权委托给描述符类
  • 类里实现了 __get__()__set__()__delete__() 的方法
    • __get__: 用于访问属性,格式:__set__(self, obj, type )它返回属性的值,若属性不存在、不合法等都可以抛出对应的异常
    • __set__:将在属性分配操作中调用,格式 : __get__(self, obj, value)不会返回任何内容
    • __delete__:控制删除操作,格式 : __delete__(self, obj)不会返回内容
  • 使用案例 :
class Score: 
    def __set__(self, instance, value):
        if not isinstance(value, int):
            raise TypeError('Score must be integer')
        if not 0 <= value <= 100:
            raise ValueError('Valid value must be in [0, 100]')
        self._score = value
    def __get__(self, instance, owner):
        return self._score
    def __delete__(self):
        del self._score

class Student:
    math = Score()
    chinese = Score()
    english = Score()
    def __init__(self, name, math, chinese, english):
        self.name = name
        self.math = math
        self.chinese = chinese
        self.english = english
    def __repr__(self):
        return "".format(
                self.name, self.math, self.chinese, self.english
            )

QWQ = Student("QWQ", 10, 12, 13)
print(QWQ)

感谢阅读 若有错误 敬请见谅 !!!


你可能感兴趣的:(python学习笔记,1024程序员节,python)