介绍

对象:类(class)和实例(instance)

  • 类:用来描述具有相同的属性和方法的对象的集合,本质上类也是对象;
    • 比如:int是整数对象的类型, str是字符串对象的类型, list是 列表对象的类型;
    • 以买手机类为例(这个类就相当于一个手机模型):
      • 通过该手机类可以产生实例对象:华为手机,iphone手机…..;
      • 手机普遍的属性可以有:品牌,颜色,价格…..;
      • 手机普遍的功能(方法)可以有:打电话,发短信,玩游戏;
  • 实例:具有属性和方法的对象,实例本质上也是对象,为了区别于整个的对象概念,把它称作实例,在python中,它就是一块内存地址;
    • 要产生一个类的实例对象,只需要将类名后面加上括号赋值给一个变量(既实例对象); 所以我们把一个个具体的对象称为该类型的实例;
    • 数字对象3是int类型的的实例,具有int类型的特征; 字符串对象abc是str类型的实例,具有str类型的特性(可以执行str的所有方法,比如 find, split等)
    • 多个对象:提取对象的共同的特征和动作,封装到一个类中;

属性:表示这个类具有的特征(以人为例:人属于类,人具有的特征有:姓名,年龄,性别,身高,血型,婚否等等….)

  • 定义一个类型就是用于描述通过该类型创建的实例所具有的公共特征,也就是说共同具体什么属性和方法;
  • 属性分类:
    • 类属性
    • 实例属性

方法:表示这个类可以执行哪些动作(打电话,看电视,刷抖音,敲代码,看书等等….)

  • 方法分类:
    • 类方法:方法上需要使用装饰器@classmethod
    • 实例方法:该方法不需要任何修饰.
    • 静态方法:方法上需要使用装饰器 @staticmethod

对象调用类方法的顺序:
先找对象自己内存空间的属性,然后再去类模板中寻找,如果自己空间中存在要找的属性,则不会再去模板中查找;

类相关

元类

概念:创建对象的类

  • 对象怎么产生?由类创建出来
  • 类是不是对象?是
  • 类对象是不是由另外一个类创建出来的?是,创建类对象的类,称之为:元类

10,面向对象(done) - 图1

  1. # type就是元类,通过type可以找到num对应的类
  2. num = 10
  3. print(type(num))
  4. # 通过type元类创建【类对象】
  5. def run(self):
  6. print(self)
  7. my_dict = type('Person',(),{'name':'张三','age':18,'run':run}) # 字典中包含了属性和方法
  8. print(my_dict) # <class '__main__.Person'>
  9. print(my_dict.__name__) # Person
  10. print(my_dict.__class__) # <class 'type'>
  11. print(my_dict.__dict__)
  12. p = my_dict()
  13. print(p)

创建类

系统内置特殊属性 描述
dict 查看类的属性
bases 查看类的所有父类构成元组
doc 查看类的文档字符串
name 查看类名
module 查看类定义所在的模块
dict 查看实例的属性
class 查看实例对应的类
  1. # 创建标准类对象【通过class关键字】
  2. class Person: # 定义一个类
  3. name = '张三' # 定义类属性:类属于所有对象的共享属性,类属性只属于类
  4. class Person1:
  5. nikename = '花花'
  6. color = '黑色'
  7. def __init__(self):
  8. name = '王五'
  9. age = 18
  10. def test1(self):
  11. pass
  12. p1 = myclass() # 通过类创建出来的实例p1
  13. print(p1)

属性相关

类属性,实例属性

注意点:

  • 在类和实例中,所有的属性和方法都是存储在类的dict中;
    • 类对象中的dict属于只读属性,不可修改,只能查询;但可以通过@setattr方法修改;
    • 实例对象中的dict中的属性可读可写;
  • 类属性和实例属性操作上的区别:
    • 存储:类属性存储在类定义中,实例属性存储在实例当中;
    • 增加操作:
      • 类属性可以直接在类中定义,也可以使用类名.属性名的方式添加
      • 实例属性必须通过类创建实例之后,再通过实例名.属性名的方式添加
    • 修改操作:
      • 类属性通过类名.属性名的方式进行修改
      • 实例属性通过实例名.属性名的方式进行修改
    • 查询操作:
      • 即可以通过类名.属性名的方式进行访问,也可以通过实例名.属性名的方式进行访问

步骤1:类属性的增、删、改、查

  • 类属性的定义可以理解为定义属性的初始值,【在面向对象中,它类似于全局变量】 ```python

    类本质也是一个对象

    class Person: # 创建类,类名要求首字母大写,多个单词使用驼峰式命名,类:就是将具有相同特性的事物可以归类成一类,可以称之为“类模版” name = ‘张三’ # 定义一个类属性,类属性属于所有对象的共享属性,类属性只属于类

通过原类名查看类的信息

print(‘类1:’,Person) # 查看类信息 print(‘类2:’,Person.name) # 查看类属性‘name’ print(‘类3:’,Person.name) # 查看类名
print(‘类4:’,Person.class) # 查看类的类型, 或者使用 print(type(Person)) print(‘类5:’,Person.dict) # 查看类的所有属性(类的内置属性和创建的属性),以字典形式返回

增加类属性

Person.age = 18 #【增加】类属性

Person.dict = {‘count’:100} # dict在类里面属于只读属性,不能修改类中的值,所以这种操作会报错

修改类名和类属性

p = Person #【修改】类名,注意:类名也是一个变量
p.name = ‘李四’ #【修改】类属性 p.age = 20

查看类属性

print(‘类属性1:’,p.name) # 通过对象访问类属性,类属性中存在这个属性,正常打印:张三 print(‘类属性2:’,p.name) print(‘类属性3:’,p.class) print(‘类属性3:’,p.dict) # dict在类里面属于只读属性,不能修改类中的值,所以这种操作会报错

删除类属性

del Person.age # 注意:del可以删除类的直系属性,但不能通过对象删除类的属性,对象只能访问类中的属性,不能对其进行操作

  1. **步骤2:实例属性的增,删,改,查**
  2. - 实例对象调用属性的顺序为:先找自己空间中的属性,然后再去模板中找,如果自己空间中存在要找的属性,则不会再去模板中查找
  3. - 通过**实例名.属性**来修改,一旦修改,如果对象空间中不存在该属性,则就会添加该属性【动态添加】
  4. - 不同对象之间的属性不能相互调用
  5. ```python
  6. class Person:
  7. name = '张三'
  8. class Person1:
  9. nikename = '李四'
  10. ## 创建实例
  11. dx1 = Person() # 根据类Person()模板,创建一个实例对象为:dx1,此时会产生一个新的内存空间给该实例对象
  12. dx2 = Person() # 根据类Person()模板,创建一个实例对象为:dx2,此时会产生一个新的内存空间给该实例对象
  13. ## 增加实例属性
  14. dx1.name = '王五' #【增加】为实例dx1增加name属性,期初dx1空间中没有name属性,则会调用类属性name,一旦增加,就会在对象dx1的内存空间中动态添加name属性
  15. dx1.age = 18 #【增加】为实例dx1再次动态的增加一些属性,语法:对象.属性 = 值
  16. ## 查看实例属性
  17. print('实例属性1:',dx1) # <__main__.Person object at 0x7fa6c8248490>
  18. print('实例属性2:',dx1.age) #【查看】是否有添加成功
  19. print('实例属性3:',dx1.name)
  20. print('实例属性4:',dx1.__class__) #【查看】访问实例对应的类,或者使用 print(type(dx1))
  21. print('实例属性5:',dx1.__dict__) #【查看】当前对象里面所有的属性,以字典形式返回
  22. # print(p.gender) # 如果自己空间中没有这个属性,类空间中也不存在这个属性,报错!
  23. ## 修改实例属性
  24. dx1.__dict__ = {'name':'赵六','age':25} # 通过传递字典的方式修改实例属性
  25. print('实例属性6:',dx1.name)
  26. print('实例属性7:',dx1.age)
  27. dx1.__class__ = Person1 # 通过__class__属性修改对象指向到其他的类
  28. print('实例属性8:',dx1.__class__)
  29. print('实例属性9:',dx1.nikename)
  30. dx1.height = [1,3] #【添加】一个对象属性height (id:140354305625296)
  31. print('实例属性10:',dx1.height,id(dx1.height))
  32. dx1.height = [4,5]
  33. print('实例属性11:',dx1.height,id(dx1.height)) #【修改】对象属性height的值,id值发生变化 (id:140354305434560)
  34. dx1.height.append(6)
  35. print('实例属性12:',dx1.height,id(dx1.height)) #【添加】值到属性中,id值没有变化,因为列表是属于可变类型 (id:140354305434560)
  36. ## 删除实例属性
  37. del dx1.height

控制实例属性的添加slots

作用:通过类来限定实例对象可以添加哪些属性

  1. class person:
  2. __slots__ = ['age','name'] # 列表中用于限制实例对象可以添加哪些属性
  3. pass
  4. p1 = person()
  5. p1.name = '张三'
  6. p1.age = 10 # 如果执行p1.num = 10 则成勋会报错AttributeError: 'person' object has no attribute 'num'
  7. print(p1.name)
  8. print(p1.age)

公共属性,受保护属性,私有属性

  • 概念:是指将一些原本公开的属性设置权限, 只能在小范围访问, 其他地方访问不了;
  • 意义:保障数据的安全性,提高代码的可维护性;
  • 注意:Python没有真正的私有化支持,但可使用_(下划线)的方式完成伪装私有化的效果,该方式同样也可以用在类属性(方法)和实例属性(方法)上;

10,面向对象(done) - 图2

  • 公有属性:共享的属性,一般的属性;
  • 受保护属性:用 _ 前缀表示;
  • 私有属性:用 __ 前缀表示,该属性只能在类的内部访问,其他地方无法访问,通过实例也不能访问私有属性,主要是用于防止在子类中被同名的属性所覆盖;

10,面向对象(done) - 图3
公有属性测试

  1. class Animal:
  2. x = 10 # 公有属性
  3. def test1(self):
  4. print('1,',Animal.x)
  5. print('2,',self.x)
  6. class Dog(Animal):
  7. def test2(self):
  8. print('3,',Dog.x)
  9. print('4,',self.x)
  10. # 类内部访问
  11. a = Animal()
  12. a.test1() # 通过实例进行类内部访问(实际上是在类内部进行访问):10
  13. # 子类内部访问
  14. d = Dog()
  15. d.test2() # 通过衍生类实例进行子类内部访问(实际上是在类内部进行访问):10
  16. # 模块内其他位置进行访问
  17. print('5,',Animal.x) # 类外部,可通过类名访问:10
  18. print('6,',Dog.x) # 类外部,可通过衍生类名访问:10
  19. print('7,',a.x) # 类外部,可通过类实例名访问:10
  20. print('8,',d.x) # 类外部,可通过衍生类实例名访问:10

受保护属性测试

  1. class Animal:
  2. _x = 10 # 受保护属性
  3. def test1(self):
  4. print('1,',Animal._x)
  5. print('2,',self._x)
  6. class Dog(Animal):
  7. def test2(self):
  8. print('3,',Dog._x)
  9. print('4,',self._x)
  10. # 类内部访问
  11. a = Animal()
  12. a.test1() # 通过实例进行类内部访问(实际上是在类内部进行访问):10
  13. # 子类内部访问
  14. d = Dog()
  15. d.test2() # 通过衍生类实例进行子类内部访问(实际上是在类内部进行访问):10
  16. # 模块内其他位置进行访问
  17. print('5,',Animal._x) # (出现警告)类外部,勉强可以通过类名访问:10
  18. print('6,',Dog._x) # (出现警告)类外部,勉强可以通过衍生类名访问:10
  19. print('7,',a._x) # (出现警告)类外部,勉强可以通过类实例名访问:10
  20. print('8,',d._x) # (出现警告)类外部,勉强可以通过衍生类实例名访问:10

私有属性测试

  1. class Animal:
  2. __x = 10 # 私有属性
  3. def test1(self):
  4. print('1,',Animal.__x)
  5. print('2,',self.__x)
  6. class Dog(Animal):
  7. def test2(self):
  8. print('3,',Dog.__x)
  9. print('4,',self.__x)
  10. # 类内部访问
  11. a = Animal()
  12. a.test1() # 通过实例进行类内部访问(实际上是在类内部进行访问):10
  13. # 子类内部访问
  14. # d = Dog()
  15. # d.test2() # 通过衍生类实例进行子类内部访问,拒绝
  16. # 模块内其他位置进行访问
  17. # print('5,',Animal.__x) # 类外部,通过类名访问,拒绝
  18. # print('6,',Dog.__x) # 类外部,通过衍生类名访问,拒绝
  19. # print('7,',a.__x) # 类外部,通过类实例名访问,拒绝
  20. # print('8,',d.__x) # 类外部。通过衍生类实例名访问,拒绝

私有属性【名字重整机制】

1,由于python并没有真正的私有化支持,只是使用下划线完成伪私有的效果,而真正的私有化,是指不管通过什么样的方式都不能访问私有属性;
2,所以python使用了名字重整机制(name mangling)来实现属性私有化,该机制就是重改x为另外一个名字,如:_类名私有属性名;
3,名字重整机制的目的:防止外界直接访问,防止子类中有同名称的属性被覆盖;

  1. class Animal:
  2. __x = 10 # 私有属性
  3. def test1(self):
  4. print('1,',Animal.__x)
  5. print('2,',self.__x)
  6. # print(Animal.__x) # 不能被访问
  7. print(Animal.__dict__) # 属性的字典中,有一个_Animal__x属性
  8. print(Animal._Animal__x) # 通过名字重整机制就可以正常访问私有属性

私有属性【应用场景】

可以实现数据保护和数据过滤

  1. class Person:
  2. def __init__(self): # 当我们创建好一个实例之后,程序会自动调取这个方法,用于初始化这个实例对象
  3. self.__age = 18 # 创建私有属性,用于【数据保护】,该属性只能在类内部被访问,所以修改的话也只能在类的内部被修改
  4. def setAge(self,value): # 既然__age只能在类内部访问,那么可以创建两个方法来操作这个私有属性__age,一个是修改,另一个是调用
  5. if isinstance(value,int) and 0 < value < 200: # 此时进行【数据过滤】
  6. self.__age = value
  7. else:
  8. print('输入数据错误,请重新输入')
  9. def getAge(self): # 既然在外部不能访问私有属性,也不建议使用名字重整机制,那么可以创建一个方法来获取被修改后的数据
  10. return self.__age
  11. p1 = Person()
  12. print(p1.__dict__)
  13. # print(p1.__age) # 报错,不能直接访问私有属性
  14. p1.__age = -10 # 添加,这里只能在实例中添加一个属性,并不会改变类中的__age = 18的值
  15. print(p1.__dict__)
  16. print(p1.__age) # 获取到添加的属性对应的值
  17. p1.setAge(220) # 调用修改方法
  18. # print(p1._Person__age) # 如果在外部强行使用私有属性,只能使用名字重整机制(不建议使用)
  19. p1.getAge() # 调用获取数据的方法

只读属性【@peoperty (可读,不可修)】

什么是只读属性: 一个属性只能读取,不能写入,一般指实例属性;
应用场景:有些属性, 只限在内部,根据不同的场景进行修改,对外界来说,,不能修改,只能读取,比如:电脑类的网速属性, 网络状态属性;
私有化+部分公开:

  • 私有化:【既不能读也不能写】,通过“属性前置双下划线”实现;
  • 部分公开:【公开读的操作】:通过公开的方式实现 【利用@property装饰器实现:在外界可以像调用属性名的方式来调用部分公开的方法名,如:p.age】
  • 带@property装饰,则可以通过p.getAge,不需要小括号的方式来调用;
    • 在外界【可读取属性,不可修改,不可增加】;
  • 不带@property装饰,则只能通过p.getAge( ),带小括号的方式来调用;

    • 在外界【可读取属性,不可修改】如果修改,只会增加了一个属性; ```python class Person: def init(self): # 【禁用读写】先将属性私有化 self.__age = 18

      def getAge(self): # 【部分公开】通过公开的方法 return self.__age

      @property # 作用:可以像调用属性的方式来调用装饰的这个方法,也就是说,方法名可以向属性名一样被调用,不需要添加小括号. def property_getAge(self): return self.__age

p = Person()

print(p.dict)

print(p.__age) # 报错,不能被访问

print(p.getAge()) # 18 print(p.property_getAge) # 18,通过读取使用@property装饰器的【实例方法名】正常获取

print(‘修改后的属性操作’) p.age = 100 # 注意这里是增加一个属性 print(p.dict) print(p.age) # 查看到的是增加后的实例属性 # 100 print(p.getAge()) # 调用实例方法(带括号) # 18

  1. <a name="7238ecb2"></a>
  2. ### 只读属性【@property(可读,可修)】
  3. **@property装饰器:**
  4. - 该方法就是将一些“属性的操作方法(就是属性的增删改查操作)" 关联到某一个属性中;
  5. ```python
  6. class Person:
  7. def __init__(self): # 【禁用读写】先将属性私有化
  8. self.__age = 18
  9. def setAge(self,value): # 定义修改属性的方法
  10. self.__age = value
  11. def getAge(self): # 定义获取属性的方法
  12. return self.__age
  13. # 将getAge方法和setAge方法关联起来 ===》 property(fget=Nofne, fset=None, fdel=None, doc=None)
  14. age = property(getAge,setAge) # 创建一个属性对象,将其与getAge方法和setAge方法关联起来,通过这种方式间接的管理了私有属性
  15. p = Person()
  16. print(p.__dict__)
  17. print(p.getAge()) # 18
  18. p.__age = 111 # 增加了一个__age的属性
  19. print(p.__dict__)
  20. print(p.getAge()) # 18
  21. print(p.__age) # 111
  22. a.getSet(99) # 修改私有属性,或者使用 p.age = 99 直接修改
  23. print(p.__dict__) # 查看对象字典
  24. print(p.getAge()) # 88
  25. print(p.age) # 查看修改后的私有属性

将getAge方法和setAge方法分解开来使用

  1. class Person:
  2. def __init__(self): # 【禁用读写】先将属性私有化
  3. self.__age = 18
  4. @property
  5. def age(self): # 定义获取属性的方法
  6. return self.__age
  7. @age.setter
  8. def age(self,value): # 定义修改属性的方法
  9. self.__age = value
  10. p = Person()
  11. print(p.age) # 查看私有属性
  12. p.age = 100 # 此时可以修改私有属性(注意:使用了装饰器,那么直接针对方法名进行传参即可修改私有属性值)
  13. print(p.__dict__) # 查看对象字典
  14. print(p.age) # 查看修改后的私有属性

只读属性【使用**setattr**方法】

  • 上面的方式中,使用属性私有化+部分公开的方式,并不是绝对的安全的;
    • 原因1:底层中,外界可以使用p._Person__age = 999 的方式修改属性;
    • 原因2:底层中,外界可以使用p.__dict__['_Person__age'] = 999 的方式进行修改;
  • 安全期间,使用 setattr 方法:

    • 首先,可以屏蔽以p._Person__age = 999 的方式去修改属性;
    • 其次,当使用 “实例.属性 = 值” 这种格式给一个实例增加或修改属性的时候,都会调用系统内置的这个方法;
    • 在这个方法的内部, 才会真正的把属性以及对应的值存储到__dict__当中; ```python class Person: “”” 当我们通过【实例.属性 = 值】的方式给一个实例增加一个属性时,或者是修改一下属性值的时候,都会调用这个方法; 在这个方法的内部,才可以真正的把这个属性,以及对应的数据,存储到dict字典里面去;
      “”” def setattr(self, key, value):
      print(key, value) # key = 属性 value = 属性对应的值

      if key == ‘age’ and key in self.dict.keys(): # 1,判断key是否是需要设置的只读属性,如果是,直接屏蔽,并且加入到dict中去【为了读取】

      1. print('该属性为只读属性')

      else: # 2,如果不是只读属性的名称,那就把该属性添加到实例属性中去

      1. self.__dict__[key] = value # 3,在字典中添加键值对

p = Person()

p.age = 18 print(p.dict) # {‘age’: 18} 被添加了进去

p.name = ‘sz’ # 再增加一个属性,被添加了进去
print(p.dict) print(p.age)

print(‘\n———下面的操作不能修改属性值———-‘) p.age = 100 # 【精华点】尝试修改dict已经存在的age属性的值,此时是修改不了的,所以打印出来还是18 print(p.age) # 18

  1. <a name="304837ac"></a>
  2. ## 方法相关
  3. <a name="22693c14"></a>
  4. ### 类方法,实例方法,静态方法
  5. **self的说明:**
  6. - 与一般函数定义不同,方法必须包含形式参数 self,且为第一个参数,self = 对象本身,代表当前对象的地址,self不是python关键字,方法中的self其实就是实例对象的唯一标志;
  7. - **方法分类:**
  8. - 说明:无论哪种类型的方法,都是存储在类的__ dict__ 字典中,没有在实例当中;
  9. - 实例方法:默认第一个参数self需要接受到一个实例,通过它来传递实例的属性和方法(也可以传类的属性和方法);
  10. - 调用:只能由实例对象进行调用,self调用实例属性:所有的实例属性都需要统一,否则程序运行时,会报错;
  11. - 类方法:使用装饰器@classmethod,默认第一个参数必须是当前类,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法);
  12. - 调用:实例对象和类对象都可以调用;
  13. - 静态方法:使用装饰器@staticmethod,没有“self”和“cls”参数,参数随意,但是方法体中不能使用类或实例的任何属性和方法;
  14. - 魔术方法
  15. **实例方法的调用**
  16. ```python
  17. class person:
  18. name = 'zhangsan'
  19. age = 18
  20. def shilifangfa(self): # 定义实例方法(self = 实例的空间地址)
  21. print('self的内存地址为:',self) # 获取到self的空间值
  22. print(self.__class__.name) # 调用类属性,使用self.__class__.类属性名
  23. print(self.__class__.age)
  24. print('{}正在吃{}'.format(self.name,self.food)) # 调用实例属性,使用self.实例属性名
  25. def run(self):
  26. self.shilifangfa() # 在实例方法之间相互调用,使用self.方法名()
  27. print('{}在院子里跑来跑去'.format(self.name))
  28. # person.shilifangfa() # 会报错 TypeError: shilifangfa() missing 1 required positional argument: 'self'
  29. print('\n============创建实例1============')
  30. p1 = person()
  31. print('对象p的地址为:',p1) # 创建实例之后获取的内存地址:0x7fa6c829c590,此时该地址和self的地址相同
  32. print(p1.__dict__) # 空的,因为当前实例中没有任何属性,注意:无论哪种类型方法,都存储在类当中,即类的__dict__字典中,没有在实例当中
  33. p1.name = '李四' # 动态的向实例p的空间中添加了name和food两个属性
  34. p1.food = '桃子'
  35. p1.shilifangfa() # 使用实例调用实例方法,注意:无传参也能打印,因为self就是对象本身
  36. print('\n============创建实例2============')
  37. p2 = person()
  38. print('对象p1的地址为:',p2)
  39. print(p2.__dict__)
  40. p2.name = '王五' # 动态添加了name和food两个属性,如果向p2空间中只是添加name属性,那么当程序运行时,会报错!因为所有实例属性没有统一
  41. p2.food = '苹果'
  42. p2.shilifangfa()

类方法的调用
由于只能访问类属性和类方法,所以如果是需要在创建对象之前完成的一些操作,可以使用类方法,因为它不依赖对象

class Person:
    name = 'zhangsan'                     
    age = 18

    @classmethod                   # 类方法装饰器
    def leifangfa(cls,num):        # 第一个参数是cls,默认为类,cls == class
        print('类方法的调用',cls,num,cls.name,cls.age)


print('\n============通过类名调用类方法============')
Person.leifangfa(123)              


print('\n============通过自定义函数调用类方法============')
func = Person.leifangfa            
func(111)


print('\n============通过实例名调用类方法============')
p = Person()                       
p.leifangfa(666) 


print('\n============通过派生类调用类方法============')
class testClass(Person):           
    pass
testClass.leifangfa(999)

静态方法的调用

class Person:
    name = 'zhangsan'                     
    age = 18

    @staticmethod                 # 静态方法装饰器
    def jingtai():
        print('静态方法的调用')

# 通过类名调用
Person.jingtai()                  

# 通过定义函数的形式来调用
func = Person.jingtai               
func()

# 通过实例来调用
p = Person()                        
p.jingtai()

三种方法访问属性的权限

class Person:
    age = 100

    @staticmethod
    def jingtaifanggfa():
        print('静态方法')
        print(Person.age)        # 静态方法:不能访问实例属性,只能访问类属性

    @classmethod
    def leifangfa(cls):
        print('类方法',cls)
        print(cls.age)           # 类方法:不能访问实例属性,只能访问类属性

    def shilifangfa(self):
        print('实例方法',self)
        print(self.age) 
        print(self.num)          # 实例方法:既能访问实例属性,也能访问类属性

p = Person()
p.num = 10

p.shilifangfa()                  

p.leifangfa()                                    

Person.jingtaifanggfa()

私有化方法

class Person:
    __age = 18                 # 定义私有属性

    def __run(self):           # 定义私有方法
        pass

p = Person()

print(Person.__dict__)

# print(p.__run())             # 在外界访问私有方法不能被调用
print(p._Person__run())        # 使用名字重整机制进行访问

内置方法

信息格式化

  • 方法: str ( )和 repr ( )

    • 该方法用于是描述一个对象的字符串, 方便用户阅读 ```python class person1:
      def init(self,name,age): self.name = name self.age = age

      def str(self): # 信息格式化1,面向的是用户,使用字符串进行实例描述, 通过print或str进行触发 return ‘姓名:{}, 年龄:{}’.format(self.name,self.age)

      def repr(self): # 信息格式化2,面向开发人员,使用字符串进行实例描述, 通过实例本身或repr触发(就是没有使用str()方法的情况下返回的结果) return ‘reprxxxx’

class person2:
def init(self,name,age): self.name = name self.age = age

先找 str ,如果没有,再找 repr

p1 = person1(‘张三’,18) p2 = person2(‘李四’,20)

print(‘使用print()函数触发str()方法:’) print(p1) # 如果定义了str方法,那么打印实例名的时候就可以直接显示该方法中返回的结果 ===》 姓名:张三, 年龄:18 print(p2) # 如果没有定义str方法,那么返回的是 <__main__.person2 object at 0x7f448edf0550> —> 面向开发人员需要查看的结果 print(type(p1)) # 返回的是对象方法里面的返回值

print(‘\n使用str()函数触发str()方法:’) print(str(p1))
print(type(str(p1))) # 返回的是字符串类型

print(‘\n使用repr()函数触发或直接使用实例名触发:’) print(repr(p1)) # 如果定义了repr方法,则获取的是该方法中返回的信息,同时,也可以用eval()方法进行再次转换 print(repr(p2)) # 如果没有定义repr方法,则获取的是实例的本质信息 p1 # 如果没有使用print或str触发,那么实例会跳过str方法,直接找repr方法;


<a name="0b94ae1f"></a>
#### 把对象当做函数调用

- 方法: ** call ** ( ) 
   - 作用:使“对象”具备有当做函数来调用的能力;
   - 使用:定义实例方法 ** call ** ( ),然后创建实例,那么实例就可以通过函数的方式进行调用;
```python
class person:

    def __init__(self,name):
        self.name = name

    def __call__(self,age):
        print('姓名:{},年龄:{}'.format(self.name,age))


p1 = person('张三')
p2 = person('李四')

p1(18)              # 使实例通过函数的形式进行调用,间接的去调用__call__(),这里18就是传入给age参数的
p2(20)
class pen:

    def __init__(self,pen_name):
        self.pen_name = pen_name

    def __call__(self,pen_color):
        print('这是一只%s的%s'%(pen_color,self.pen_name))


gangbi = pen('钢笔')
gangbi('黑色')              # 间接的调用了__call__()方法

maobi = pen('毛笔')
maobi('白色')

对实例进行索引

  • 方法有:setitem( ), getitem( ), delitem( )

    • 对一个实例进行索引 ```python class person:

      def init(self): self.cache = {} # 增加一个字典属性

      def setitem(self, key, value): # 设置/增添键值 print(‘setitem:’,key,value) self.cache[key] = value

      def getitem(self, item): # 获取键值 print(‘getitem:’,item) return self.cache[item]

      def delitem(self, key): # 删除操作 print(‘setitem:’,key) del self.cache[key]

p = person()

p[‘name’]=’sz’ # setitem print(p[‘name’]) # getitem

del p[‘name’] print(p.cache) # 查询字典


<a name="dc903f95"></a>
#### 对实例进行切片

- 方法有:setitem( ), getitem( ), delitem( ) 
   - 对一个实例进行切片操作
```python
class person:

    def __init__(self):
        self.items = [1,2,3,4,5,6,7,8,]        # 初始化以列表形式

    def __setitem__(self, key, value):       # 切片时只能修改,不能新增
        print(key,value)
        print(key.start)
        print(key.stop)
        print(key.step)
        self.items[key] = value

        #if isinstance(key,slice):             # 容错处理
        #    self.items[key.start:key.stop:key.step]=value

    def __getitem__(self, item):               # 获取信息
        print('getitem',item)

    def __delitem__(self, key):                # 删除
        print('setitem',key)


p = person()
print(p)
print(p.items)

p[0:4:2] = ['a','b']                       # 对进行切片操作,修改列表
print(p.items)

比较大小和真假

  • 如果对于反向操作的比较符, 只定义了其中一个方法,但使用的是另外一种比较运算,那么, 解释器会采用调换参数的方式进行调用该方法,例如:
    • 定义了 “小于” 操作:x < y,使用 x > y,会被调换参数,调用上面的 “小于操作”.
  • 但是, 不支持叠加操作,例如:

    • 定义了 “小于” 和 “等于” 操作,不能使用 x <= y ```python class person:

      def init(self,age,height): self.age = age self.height = height

      def eq(self,other): # 实现相等操作 print(other) return self.age == other.age # 返回操作后的值

      def ne(self, other): # 不相等 print(‘xxx’)

      def gt(self, other): # 大于 pass

      def ge(self, other): # 大于等于 pass

      def lt(self, other): # 小于 print(‘lt’)

      def le(self, other): # 小于等于 pass

      def bool(self): # 布尔类型,返回实例是True还是False return self.age > 18 # 返回值控制实例是True还是False

p1 = person(18,180) p2 = person(17,190)

print(p1 == p2) print(p1 != p2) print(p1 < p2)


<a name="24eb9b72"></a>
#### 对实例进行迭代

- 方法有:getitem( ), iter( ), next( )
- 类中同时有iter和next方法的实例才是一个迭代器,利用iter获取迭代器对象,利用next访问迭代器
```python
# __getitem__
class person:
    def __init__(self):
        self.result = 1

    def __getitem__(self, item):
        self.result += 1
        if self.result >= 6:
            raise StopIteration('停止遍历')
        return self.result

p = person()
for i in p:
    print(i)
# __iter__ 和 __next__
class person:
    def __init__(self):
        self.result = 1

# iter优先级高于getitem
    def __iter__(self):       # 获取实例的迭代器
        print('iter')
        return self           # 返回是一个迭代器对象

    def __next__(self):        # 对于迭代器调用next方法
        self.result += 1
        if self.result >= 6:
            raise StopIteration('停止遍历')
        return self.result                  # 对于迭代器进行遍历

p = person()
for i in p:
    print(i)

描述器

@property

  • 描述器:

    • 概念:描述一个属性操作的对象(一个类里面,有一个属性,这个属性指向一个特殊的对象,只要这个特殊对象实现3个实例方法【 set , get , delet 】,它就是描述器)
    • 作用:

      • 可以代为管理一个类属性的读写删操作,在相关方法中,对数据进行验证,过滤处理等(如果一个类属性被定义为描述器,那么以后对这个类属性的操作(读写删),都将由这个描述器代理) ```python

        使用property类将属性的增删改查关联在一起,生成一个描述器对象

        class Person(object): def init(self): self.__age = 18

      def get_age(self): return self.__age

      def set_age(self,value): self.__age = value

      def del_age(self): del self.__age

      property(fget=None, fset=None, fdel=None, doc=None)

      xage = property(get_age,set_age,del_age) # xage是一个类属性,当前就是一个描述器

p = Person()

print(p.xage) p.xage = 100 # 修改私有属性值 print(p.xage) print(p.dict) del p.xage

help(Person)

使用@property装饰器分解式定义属性的增删改查方法

print(‘\n=================================’) class Person2(object): def init(self): self.__age = 18

@property
def xage(self):
    return self.__age

@xage.setter
def xage(self,value):
    self.__age = value

@xage.deleter
def xage(self):
    del self.__age

p2 = Person2() print(p2.xage)

p2.xage = 200 print(p2.xage) print(p2.dict)


<a name="eeede18e"></a>
#### 将3个方法归在一个类中

- @property的方式需要通过3个方法来描述一个属性,如果有3个属性需要被描述,那么就要定义9个方法,则整个类中定义的方法就显得比较多。
- 一个实例属性的正常访问顺序: 
   - 实例对象自身的__**dict**__字典;
   - 对应类对象的__**dict**__字典;
   - 如果有父类, 会再往上层的__**dict**__字典中检测;
   - 如果没找到, 又定义了__**getattribute**__方法, 就会调用这个方法;
- 在上述的整个过程当中, 是如何将描述器的__**get**__方法给嵌入到查找机制当中? 
   - 就是通过__**getattribute**__方法进行实现; 
      - 如果实现了描述器方法,__**get**__就会直接调用;
      - 如果没有实现了描述器方法, 则按照上面的机制去查找;
```python
class Age:            # 创建一个描述器的类,然后定义3个方法
    def __get__(self, instance, owner):
        print('get')

    def __set__(self, instance, value):
        print('set')

    def __delete__(self, instance):
        print('delete')

class person:
    xage = Age()       # 最后在其他类中通过创建一个实例来生成一个类属性(描述器)
    def __getattribute__(self, item):
        print('xxxxxxxxxxx')

# 通过实例操作描述器(三个方法都可以被调用)
p = person()

p.xage = 10                 # 调用了set,get
print(p.xage)               # 调用的方法中没有返回值

del p.xage                  # 调用了delete

print('\n================================')

# 通过类本身操作描述器(最多只会调用get方法)【一般不使用这种方式调用】
print(person.xage)          # 只是调用了get方法
person.xage = 19
del person.xage             # 没有用delete方法

生命周期和方法

  • 生命周期:
    • 指的是一个对象, 从诞生到消亡的过程
    • 当一个对象被创建时, 会在内存中分配相应的内存空间进行存储
    • 当这个对象不再使用, 为了节约内存, 就会把这个对象释放
  • 涉及到的问题:
    • 如何监听一个对象的生命过程?
    • Python是如何掌控一个对象的生命?
  • 监听对象生命周期的方法:
    • new( ):
      • 当我们创建一个对象时, 用于给这个对象分配内存的方法,通过拦截这个方法, 可以修改对象的创建过程,比如:单例设计模式
      • new: 对象的创建,是一个静态方法,第一个参数是cls,(想想也是,不可能是self,对象还没创建,哪来的self)
    • init( ):
      • 每个对象实例化的时候,都会自动执行这个方法,可以在这个方法里面,初始化一些实例属性;
      • init : 对象的初始化, 是一个实例方法,第一个参数是self
    • del( ):
      • 当对象被释放的时候调用这个方法,可用于在这个方法中清理资源;
  • 先有创建,才有初始化,即先new,而后init

new( )

class person:
    def __new__(cls):
        print('新建了一个对象,但是被我拦截了')

p = person()
print(p)   # 内存地址没有打印出来,而是被new方法拦截了

init ( )

  • 当多个对象之间在调用实例方法时,如果这些对象在属性不统一的情况下,为了让每个self执行都不会报错,那么可以使用init方法定义一份共享的方法出来;
  • 该方法是在通过类构成对象之前被执行的;
  • 如果类中没有定义这个方法:则执行开辟内存空间给对象 xx;
  • 如果类中定义了这个方法:则会进入init方法,自动调用里面的方法【注意:这里的self地址就是即将开辟给对象的空间地址】, 最后才将内存地址赋值给对象 xx;
  • init 方法可以有参数,参数通过init ( ) 传递给类的对象;
  • 该方法中定义的都是共享属性, 有了共享属性,就不用担心创建出来的对象会少了某些属性而报错;
  • 统一标准:在实例方法中定义或被调用的属性,应该同时在init方法中被定义; ```python class myclass: i = 21313

    def test(self):

      return 'hello world'
    
def __init__(self,realpart):
    self.realpart = realpart           
    print(self.realpart)

x = myclass(1000) # init起到的作用,打印出了1000

print(x.test) print(x.test()) print(x.realpart)


```python
# 传递参数到实例方法中
class Student:
    name = 'zhangsan'           
    age = 18
    food = '哈密瓜'

    def __init__(self,name,age,food):        # 构造函数,实例化的时候会自动调用该函数
        self.name = name                     # 构造函数里定义的字段称为实例属性
        self.age = age
        self.food = food

    def eat(self,food):            
#         self.food = food
        print('{}今年{}岁,她正在吃{}\n'.format(self.name,self.age,self.food)) 
        print('{}今年{}岁,她正在吃{}\n'.format(Student.name,Student.age,Student.food)) 
        print('{}今年{}岁,她正在吃{}\n'.format(self.name,self.age,Student.food))         

# xh.name = 'xiaohong'                     # 如果没有添加对象的属性,则使用类属性  
xh = Student('小红',18,'香蕉').eat('西瓜')   # 在构建对象的时候, 传递两个实参, 那么系统就会将这两个参数传递到__init__方法中

xm = Student('小明',20,'苹果').eat('桃子')   # xm对象中如果没有定义food属性,那么它会使用__init__方法中的属性

del( )

class person:

    # def __new__(self):
        # print('新建了一个对象,但是被我拦截了')

    def __init__(self):
        self.name = 'zs'
        print('这个是初始化方法')

    def __del__(self):
        print('这个对象已经被释放')            # 对象调用完成后,实例对象会被系统自动释放,不再占用内存空间,所以这段话会被打印出来

p = person()
print(p)   
print(p.name)

综合小案例(重点关注)

  • 定义一个Person类,打印输出这个Person产生了多少个实例,创建一个 +1,删除一个 -1 ```python class person: __personCount = 0 # 定义这个统计属性为类属性,因为增加和删除实例属性,该属性都不会受到影响

    def init(self):

      print('计数 +1')   # 当创建一个实例的时候,初始化就显示+1
      self.__class__.__personCount += 1   # 通过实例调用类属性
    
def __del__(self):
    print('计数 -1')
    person.__personCount -=1            # 通过类调用类属性

@classmethod 
def show(cls):             # 为了在外部方便调用查看,可将查看的动作封装在一个方法中
    print('当前存在的实例数量为{}'.format(cls.__personCount))

创建一个实例

p = person() p1 = person() person.show()

删除一个实例

del p del p1 person.show() ```

内存管理机制