# 对象的基本理论 # 什么是对象? # 万物皆对象 # 对象是具体物体 # 拥有属性 # 拥有行为 # 把很多零散的东西,封装成为一个整体 # 举例:王二小 # 属性 # 姓名 # 年龄 # 身高 # 体重 # 行为 # 走路 # 吃饭 # 放羊 # Python中的体现 # 是一门特别彻底的面向对象编程(OOP)的语言 # 其他语言:基本数据类型,对象类型 # python全部类型都是对象类型 # 面向对象是面向过程的封装 # 对象和类的关系:对象 可以抽象为类 类可以实例化为对象 class Money: pass print(Money) #<class ‘__main__.Money‘> one = Money() print(one, type(one)) #<__main__.Money object at 0x030CFBB0> <class ‘__main__.Money‘> class Person: pass # 对象的操作:增 p = Person() # 查看对象的所有属性 print(p.__dict__) #{} p.age = 18 p.height = 180 print(p.__dict__) #{‘age‘: 18, ‘height‘: 180} # 对象的操作:删 del p.age # 对象的操作:改 p.height = 185 # 对象里面如果有这个属性,就是修改,否则就是增加这个属性 # 类里面的 __dict__ 不可以修改,只读的 # 对象里的 __dict__ 可以修改 class Test: age = 18 #Test.__dict__["age"] = 20 #TypeError: ‘mappingproxy‘ object does not support item assignment one = Test() one.__dict__ = {"name":"abc", "height":180} # __dict__字典里面生存储属性的变量 print(one.__dict__) #{‘name‘: ‘abc‘, ‘height‘: 180} # 理解下面的内容 class Person: age = 10 p = Person() print(Person.age) # 10 print(p.age) # 10 p.age += 5 # p.age = p.age + 5 # 要理解这个在对象和类的操作方法:首先是计算右边的值,p.age 在对象p中没有,所以他要到类中去取age = 10 # 然后变成10+5 = 15, 然后在p对象中新增加个age属性(以前说过,对象没有就是新增,有的话就是修改) print(Person.age) # 10 print(p.age) # 15 # __slots__ class Person: __slots__ = {"age"} #只能加入age的属性 p1 = Person() p1.age = 18 print(p1.age) # 方法:实例方法,类方法,静态方法 class Person: def eat(self): print("这是个实例方法", self) @classmethod def eat2(cls): print("这是个类方法", cls) @staticmethod def eat3(): print("这是个静态方法") p = Person() print(p) p.eat() p.eat2() p.eat3() # <__main__.Person object at 0x032F54F0> # 这是个实例方法 <__main__.Person object at 0x032F54F0> # 这是个类方法 <class ‘__main__.Person‘> # 这是个静态方法 print("-" * 30) #另外的调用方式: Person.eat("123") #这里必须要有个参数,才能调用成功 Person.eat2() Person.eat3() print("-" * 30) # 实例方法,类方法,静态方法的访问权限问题(包括类属性,实例属性) class Person: age = 10 # 实例方法 def shilie(self): print(self) print(self.age) print(self.num) # 类方法 @classmethod def leifangfa(cls): print(cls) print(cls.age) print(cls.num) # 不可以使用实例属性 # 静态方法 @staticmethod def jingtai(): print(Person.age) # 能使用类里面的属性,但很少这样去调用的 p = Person() p.num = 11 p.shilie() # 实例方法:可以调用实例属性和类属性 #p.leifangfa() # 类方法:不可以使用实例属性 p.jingtai() # 静态方法:可以使用类属性,不可以使用实例属性 # 元类 num = 123 str = "abc" print(num.__class__.__class__) # <class ‘type‘> print(str.__class__.__class__) # <class ‘type‘> print(Person.__class__) # <class ‘type‘> print(Person.__class__.__class__)# <class ‘type‘> print(p.__class__.__class__) # <class ‘type‘> # ----------------------------另外一种创建类的方式------------------------- def run(self): print(self) dog = type("Dog",(),{"name":"abc","run":run}) #字典里面可以是类属性,或者类方法 print(dog.name) #abc print(dog.run) #<function run at 0x02B56C90> # ---------------------------类的创建流程---------------------------------- class Animal: # __metaclass__ = xxx pass class Person(Animal): # __metaclass__ = xxx pass # -------------------------类的描述, pydoc 模块--------------------------------------- class Person: """ 关于类的描述,类的左右,类的构造函数 Attribute: 属性的描述 """ def run(self, distence, step): """ 函数的作用 :param distence:参数含义,数据类型,默认值 :param step: :return: 返回值类型 """ return distence / step # help(Person) # 可以这样看帮助文档 # ------------------------ 私有化属性(类的内部,子类内部,模块内部,模块外部)4大块 ------- class Animal: x = 10 _y = 20 __z = 30 def test(self): print(Animal.x) print(self.x) print(Animal._y) print(self._y) print(Animal.__z) print(self.__z) print("-" * 30) class Dog(Animal): def test2(self): print(Dog.x) print(self.x) print(Dog._y) print(self._y) # print(Dog.__z) # 错误 # print(self.__z) # 错误 print("-" * 30) # 类的内部访问 a = Animal() a.test() # 成功打印x # 子类的内部访问 d = Dog() d.test2() # 成功打印x # 模块内部访问 # print(Animal.x) # 成功打印x # print(Dog.x) # 成功打印x # print(a.x) # 成功打印x # print(d.x) # 成功打印x # print(a.__z) # 有错误 # # print(Animal._y) # 成功打印_y,但有警告 # print(Dog._y) # 成功打印_y,但有警告 # print(a._y) # 成功打印_y,但有警告 # print(d._y) # 成功打印_y,但有警告 # print(a.__z) # 有错误 # print(d.__z) # 有错误 print(Animal.__dict__) print(Animal._Animal__z) # 伪私有 30 # 模块外的访问 import public print(public.num) # 成功打印num print(public._num2) # 另外一种模块导入方法 from public import * # 直接写public 模块中的变量 print(num) # 成功打印num # print(_num2) # 有错误 # ------------------------ 私有化属性应用场景 --------------------------------- # 数据保护,数据过滤 class Person: def __init__(self): self.__age = 18 def setAge(self, value): if isinstance(value, int) and 0 < value < 200 : self.__age = value else: print("you input data error!!!") def getAge(self): print("you age is ", self.__age) return self.__age p = Person() p.setAge(100) print(p.getAge()) # --------------- 私有化的2个规范--------------------- # 1. x_ 这样命名的主要是区分系统的关键字,但又想用这个名字,所以在下面加下划线,比如 int_, class_ # 2. __x__ 这种命名的主要是系统的内置命名功能的变量 # --------------- 只读属性------------------------- # __x 这样在类中命名的变量,既不能在实例属性中读取和修改,除非通过间接的办法来实现; # 比如在类中通过写个函数来读取类中的 __x 变量;如果现在来改写一下只能读取的方式; class Person: def __init__(self): self.__age = 18 @property def age(self): # 这样就可以像正常通过实例属性来访问,但却不能通过实例属性来修改和增加 return self.__age p = Person() print(p.age) # 18 # p.age = 111 # AttributeError: can‘t set attribute # 上面的只读属性,也不是安全的,可以通过设置__dict__来修改 # p.__dict__["_Person__age"] = 999 p._Person__age = 888 print(p.age) # 888 # --------------新式类(python 3.xx,继承object) 和 经典类(python 2.xxx 默认形式不继承object) ---------- class Person: pass print(Person.__bases__) # (<class ‘object‘>,) 3.xx默认情况 # 也可以显示的显示如下,最好这样写,可以兼容2.xx: class Person(object): pass print(Person.__bases__) # (<class ‘object‘>,) # ------------- 设置只读属性的正确方法 --------------------------- class Person: # 需要只读的key 列表 attr_key = ["age","name"] # 当我们通过:实例.key = value 操作的时候都会调用这个方法 __setattr__ # 在这个方法的内部,在会把key : value 这些值存储到__dict__对象里面 def __setattr__(self, key, value): print(key, value) if key in self.attr_key and key in self.__dict__.keys() : print("this is {} attrute only read ".format(key)) else: # self.key = value # 这样写会有问题,涉入死循环 self.__dict__[key] = value p = Person() p.age = 18 #执行到这里,就会打印 age 18 print(p.age) # 18 print(p.__dict__) # {‘age‘: 18} p.age = 19 # 这里赋值后就会提示上面的错误 this is age attrute only read # --------------------------内置特殊属性---------------------- #类属性 # __dict__ 类的属性 # __bases__ 类的所有父类构成的属性 # __doc__ 类的文档字符串 # __name__ 类名 # __module__ 类定义中的模块 # __str__ 类定义字符串 #__call__ 实例直接调用 p() #实例属性 # __dict__ 实例属性 # __class__ 实例属性对应的类 class Person: def __call__(self, *args, **kwargs): print(args,kwargs) def __str__(self): return "这个是个人类的类(字符串)" def __repr__(self): return "这是个_repr_" p = Person() p() # 类中有了__call__,就可以这样调用了,打印() {} p("123","abc", name="cctv") #(‘123‘, ‘abc‘) {‘name‘: ‘cctv‘} print(p.__str__()) #这个是个人类的类(字符串) print(p) #这个是个人类的类(字符串) print(p.__repr__()) #这是个_repr_ print(repr(p)) #这是个_repr_ # ------------------------ 索引操作 -------------------------------- class Person: def __init__(self): self.cache = {} def __setitem__(self, key, value): print(key, value) self.cache[key] = value def __getitem__(self, key): print(key) return self.cache[key] def __delitem__(self, key): print(key) del self.cache[key] p = Person() p["name"] = "abc" print(p["name"]) del p["name"] print(p.cache) #{} # --------------------- 比较操作 ----------------------------------- class Person: # == ,!= , >, >= , < , =< def __init__(self, age, height): self.age = age self.height = height def __eq__(self, other): # print(self.age, other.age) return self.age == other.age def __ne__(self, other): return self.age != other.age def __gt__(self, other): #大于 pass def __ge__(self, other): #大于等于 pass def __lt__(self, other): #小于 pass def __le__(self, other): #小于等于 pass p1 = Person(18, 180) p2 = Person(18, 190) print(p1 == p2) # True print(p1 != p2 ) # False # -------------------- 上下文的布尔判断 ----------------------------- class Person: def __init__(self): self.age = 19 def __bool__(self): return self.age >= 18 p1 = Person() if (p1): print("p Ture") # p Ture # --------------------遍历操作 -------------------------------------- class Person: def __init__(self): self.result = 0 # 和上面的索引操作一样 def __getitem__(self, item): self.result += 1 if (self.result > 5 ): raise StopIteration("停止遍历") return self.result # 这个要优先于__getitem__ 运行 def __iter__(self): self.result = 0 # 迭代器次重复使用 return self def __next__(self): self.result += 1 if (self.result > 5): raise StopIteration("next停止遍历") return self.result p = Person() for v in p : print(v) # 1 2 3 4,5 # ----------------------- 描述器 --------------------------- # 方法一:举例 class Person: def __int__(self): self.__age = 18 def get_age(self): return self.__age def set_age(self, value): if value < 0 : value = 0 self.__age = value def del_age(self): del self.__age # 方法一 age = property(get_age, set_age, del_age) p = Person() p.age = 10 # 一定要赋值,不然下面调用就会错误 print(p.age) class Person: def __int__(self): self.__age = 18 @property def age(self): return self.__age @age.setter def age(self, value): if value < 0: value = 0 self.__age = value @age.deleter def del_age(self): del self.__age p = Person() p.age = 20 # 一定要赋值,不然下面调用就会错误 print(p.age) # -------------------方法二------------------------ class Age: def __get__(self, instance, owner): print("get") return instance.v def __set__(self, instance, value): print("set") instance.v = value def __delete__(self, instance): print("delete") class Person: age = Age() p = Person() p.age = 10 print(p.age) # -------------------使用类,实现装饰器 ----------------------- # 以前学过通过函数来实现装饰器,如: def checkLogin(func): def inner(): print("正在登陆认证") func() return inner @checkLogin def fashuoshuo(): print("发说说") fashuoshuo() #正在登陆认证 发说说 # 现在换成类的形式 class Check: def __init__(self, func): self.f = func def __call__(self, *args, **kwargs): print("正在登陆认证") self.f() @Check def fatupian(): print("发图片") fatupian() # 正在登陆认证 发图片 # ---------- 几个监听对象生命周期的方法------------- class Person: # def __new__(cls, *args, **kwargs): # print("新建一个对象的时候, 但被我拦截了") def __init__(self): print("这个类被初始化了") def __del__(self): print("这个对象被释放了") p = Person() # 新建一个对象的时候, 但被我拦截了 print(p) # None p = Person() #这里要把 __new__ 给注释掉,不然会被拦截 # ---------- 几个监听对象生命周期的方法:小案例------------- # Person, 打印一下,当前这个时刻,由Person类,产生的实例,有多少个 # 创建了一个实例,计数+1,如果删除了一个实例 计数-1 class Person: __personCount = 0 def __init__(self): self.__class__.__personCount += 1 # 和下面的Person.__personCount 的方法是一样的 def __del__(self): # Person.__personCount -= Person.__personCount - 1 有些问题 self.__class__.__personCount -= 1 @staticmethod def log(): # print("当前创建人的实例有{}个".format(Person.__personCount)) print("当前创建人的实例有%d个"%Person.__personCount) # 或者写成下面的形式 @classmethod def log2(cls): print("当前创建人的实例有%d个" % cls.__personCount) p = Person() p2 = Person() Person.log() # 当前创建人的实例有2个 Person.log2() # 当前创建人的实例有2个 del p2 Person.log() # 当前创建人的实例有1个 Person.log2() # 当前创建人的实例有1个 # ----------------对象 内存管理机制 存储 --------------------- num1 = 1 num2 = 1 print(id(num1), id(num2) ) #相同数值的变量地址一样,访问的是同一个地址,这是Python的机制决定的,优化访问时间 class Person: pass p = Person() p2 = Person() print(id(p), id(p2)) # 打印地址不一样 # ----------------引用计数器 --------------------- import sys # class Person: pass p1 = Person() # 获得对象的引用次数 print(sys.getrefcount(p1)) # 2 p2 = p1 print(sys.getrefcount(p1)) # 3 print(sys.getrefcount(p2)) # 3 del p1 del p2 # ------------- 引用计数器机制-特殊场景--循环引用问题----------- # 内存管理机制:引用计数器机制 + 垃圾回收机制 # 当一个对象被引用时 +1, 删除一个引用 -1 ,0:自动释放 # objgraph # objgraph.count(), 可以查看,垃圾回收器,跟踪的对象个数 import objgraph class Person: pass class Dog: pass p = Person() d = Dog() p.pet = d d.master = p print(objgraph.count("Person")) # 1 print(objgraph.count("Dog")) # 1 del p del d print(objgraph.count("Person")) # 1 这样就造成了内存泄露 print(objgraph.count("Dog")) # 1 # --------------内存管理机制 垃圾回收 如何检测循环引用 ----------- # --------------垃圾回收机制 分代回收 ------------ # --------------垃圾回收机制中 新增的对象个数 - 消亡的对象个数 达到一定个数才会触发垃圾检测 import gc #查看阀值,700个对象,10次 print(gc.get_threshold()) # (700, 10, 10) gc.set_threshold(200,5,5) # 设置参数 print(gc.get_threshold()) # (200, 5, 5) # --------------垃圾回收机制 触发时机(自动,和手动) import gc # 自动回收 # 查看垃圾回收机制是否开启,默认是开启的 print(gc.isenabled()) # True # 达到一定的阀值才会执行 # 关闭垃圾回收机制 gc.disable() # 手动回收 import gc import objgraph class Person: pass class Dog: pass p = Person() d = Dog() p.pet = d d.master = p del p del d gc.disable() #关闭垃圾回收后,下面语句也有效果 # 通过“引用计数器机制”无法回收;需要借助“垃圾回收机制”进行回收 gc.collect() # 参数有:空,0,1,2 空代表全部代的回收,0,代表0代回收,2,代表0,1,2代都回收 # 查看 print(objgraph.count("Person")) # 0 print(objgraph.count("Dog")) # 0