摘要:魔法方法类构造方法魔法方法初始化对象创建对象的过程创建一个对象解释器会自动的调用方法返回创建的对象的引用,给实例实例化执行该方法,返回值。当引用计数为时,该对象生命就结束了。
define class
class的三个组成部分:
类的名称:类名
类的属性: 一组数据
类的方法:允许对进行操作的方法(行为)
定义
class Student (object): pass
class后面定义类名(类名通常是大写开头的单词)
(object),表示该类是从哪个类继承下来的
实例化
创建实例是通过类名+()实现
stu = Student()
class Stu (): # 定义class age = 10 # 属性 def show (self): # 方法 print(self.age) # 类中获取属性 print(self, "self") print(stu.name) # 获取类外添加属性 stu = Stu() # 实例化 stu.name = "sf" # 添加属性 stu.show() # 调用方法self
self当前实例化的对象
在定义函数的时候,第一个参数需要self
class Stu (): def show_name (self): print(self.name) stu = Stu() stu.name = "sf" stu.show_name()
self在定义时需要定义,但是在调用时会自动传入。
self的名字并不是规定写死的,但是最好还是按照约定是self。
self总是指调用时的类的实例。
init魔法方法:
["__class__", "__delattr__", "__dict__", "__dir__", "__doc__", "__eq__", "__format__", "__ge__", "__getattribute__", "__gt__", "__hash__", "__init__", "__init_subclass__", "__le__", "__lt__", "__module__", "__ne__", "__new__", "__reduce__", "__reduce_ex__", "__repr__", "__setattr__", "__sizeof__", "__str__", "__subclasshook__", "__weakref__"]
__init__: 类构造方法(魔法方法)
class Stu (): # 初始化对象 def __init__ (self, new_name, new_age): self.name = new_name self.age = new_age def show (self): print("name: %s, age: %d" % (self.name, self.age)) stu = Stu("sf", 23) stu.show()
创建对象的过程:
创建一个对象
Python解释器会自动的调用__init__方法
返回创建的对象的引用,给实例
__str__: 实例化执行该方法,返回值。
当需要print一个类的时候,需要先在类中定义__str__方法,返回值,就是print()输出的值
class Stu (): def __init__ (self, new_name): self.name = new_name def __str__ (self): return self.name
__new__: 方法主要是当继承一些不可变的class时(比如int, str, tuple), 提供一个自定义这些类的实例化过程.
class Stu(object): def __new__(cls): return object.__new__(cls) # 自定义实例化过程 # 自身没有能力创建实例,可以让父类创建 def __init__(self): print("init") stu = Stu()
创建单例对象
class Single(object): __instance = None def __new__(cls): if cls.__instance != None: return cls.__instance else: cls.__instance = object.__new__(cls) return cls.__instance s1 = Single() s2 = Single()私有方法和私有属性
私有属性
按照约定俗成的规定__开头的属性表示私有属性, 不可以直接类名.变量名访问
在类中存储的形式为:_Stu__age, _类名__变量名
class Stu(): def __init__(self, new_name): self.name = new_name self.__age = 0 # 定义了一个私有的属性,属性的名字是`__age`
在类中访问形式:self.__变量名
私有方法(private)
按照约定俗成的规定__开头的属性表示私有方法, 不可以直接类名.方法名访问
存储的形式为:_Stu__get_age, _类名__方法名
class Stu(): def __test(self): # 定义私有方法 pass
在类中调用私有方法:self.__方法名()
有些时候,会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
del
__del__: 当删除一个对象时,python解释器会默认调用一个魔术方法,__del__()
class Stu(): def __del__ (self): print("remove obj") stu = Stu() del stu
在类的生命周期中,如果类销毁了,python会自动调用__del__方法。也就是说,不管是手动调用del还是由python自动回收都会触发__del__方法执行。
对象引用个数
模块sys中有一个getrefcount方法可以测试对象的引用个数
返回的结果,会比实际结果大1.
import sys sys.getrefcount("变量/方法")继承
class Animal(object): def run(self): print("Animal is running") class Dog(Animal): # 继承 def run(self): print("Dog is running") class Cat(Animal): pass dog = Dog() cat = Cat() dog.run() cat.run()
当子类和父类都存在相同的run()方法时,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run() -- 多态
重写
重写父类的方法,继承之后,子类定义和父类方法名一样的方法
class Animal(object): def run(self): print("Animal is running") class Dog(Animal): def run(self): # 重写 print("Dog is running") class Cat(Animal): pass dog = Dog() cat = Cat() dog.run() cat.run()
调用父类方法
类名调用
super关键字调用
class Animal(): def run(self): print("Animal is running") class Dog (Animal): def say(self): # 第一种,类名调用。 # Animal.run(self) #方法必须传递参数`self` # 第二种,super关键字 super().run() print("Gog is running") dog = Dog() dog.say()
私有方法,私有属性在继承中的表现:
私有方法并不会被继承(子类外和子类内都不会让使用)
私有属性并不会被继承(子类外和子类内都不会让使用)
多继承
子类具有多个父类
class A(object): # object是所有最终类的终点 def test(self): print("A") class B: def test(self): print("B") class C(A, B): # 多继承 (如果继承类中方法名或者属性名相同,生效的是参数先后顺序,`类名.__mro__`中的顺序) pass print(C.__mro__) # (多态, , , ) # C3算法 c = C() # 子类也会重写父类的方法
定义时的类型和运行时的类型不一样。
执行的时候确定
class D(object): def _print(self): print("D") class X(D): def _print(self): print("X") def introduce(temp): temp._print() d = D() x = X() introduce(d) introduce(x)
isinstance()判断一个对象是否是某种类型
实例属性属于各个实例所有,互不干扰;
类属性属于类所有,所有实例共享一个属性;
不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误。
类属性 & 实例属性
class A(): num = 1 # 类属性 def __init(self): print(self.num) a = A() a.name = 100 # 实例属性 print(A.num) # 获取类属性
实例方法 & 类方法 & 静态方法
class A(): # 定义类方法 @classmethod def add_num(cls): # 保存类的引用 pass def get_num(self): # 实例方法 pass @staticmethod def set_num(): # 静态方法 # 可以没有任何参数引用 pass a = A() A.add_num() # 调用类方法 # a.add_num() # 实例调用类方法 A.set_num() # 调用静态方法 a.set_num() # 实例调用静态方法
私有化
xx: 公有变量
_x: 单前置下划线,私有化属性或方法,from somemodule import *禁止导入,类对象和子类可以访问
__xx: 双前置下划线,避免与子类中的属性名命名冲突,无法在外部直接访问
__xx__: 双前后下划线,命名空间的魔法对象或属性。__init__。(开发时,不要使用这种定义变量方式)
xx_: 单后置下划线,用于避免与Python关键词的冲突
property
作用:获取与设置自动调用方法
类属性方式调用property:
class Money(object): def __init(self): pass def getMoney(self): pass def setMoney(self, value): pass money = property(getMoney, setMoney) # key 为调用时候的key m = Money() m.money = 10
装饰器方式使用property:
class Money(object): def __init__(self): self.__num = 0 @property def money(self): # 函数名和调用key对应 # 获取 return self.__money @money.setter def money(self, value): # 获取 self.__num = value m = Money() print(m.money)垃圾回收
小整数对象池
作用:为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间。
Python对小整数的定义是[-5, 257)这些整数对象都是提前建立好的,不会被垃圾回收。在一个Python的程序中,所有位于这个范围内的整数使用的都是同一个对象。
单个字母也是这样,但是定义2歌相同字符串时,引用计数为0,触发垃圾回收。
小整数[-5,257)共用对象,常驻内存
单个字符共用对象,常驻内存
大整数对象池
每一个大整数,均创建一个新的对象。
大整数不共用内存,引用计数为0,销毁
数值类型和字符串类型在Python中都是不可变的,意味着无法修改这个对象的值,每次对变量对修改,实际上是创建一个新的对象
intern机制
a1 = "HelloWorld" a2 = "HelloWorld" a3 = "HelloWorld"
intern机制,让它只占用一个”HelloWorld”所占的内存空间。靠引用计数去维护何时释放。
单个单词,不可修改,默认开启intern机制,共用对象,引用计数为0,则销毁
字符串(含有空格),不可修改,没开启intern机制,不共用对象,引用计数为0,销毁
Garbage collection(GC垃圾回收)
Python采用对是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略
引用计数机制
Python里每一个东西都是对象,它们的核心就是一个结构体: PyObject
typedef struct_object { int ob_refcnt; // 引用计数 struct_typeobject *ob_type; } PyObject;
PyObject是每个对象必有的内容,其中ob_refcnt就是作为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少。
当引用计数为0时,该对象生命就结束了。
引用计数机制的优点:
简单
实时性: 一旦没有引用,内存就直接释放了。不用像其它机制等待特定实际。
处理回收内存的时间分摊到了平时。
引用计数机制的缺点:
维护引用计数消耗资源
循环引用
垃圾回收机制
Python中的垃圾回收是以引用计数为主,分代收集为辅
导致引用计数+1的情况
对象被创建,例如: a = 23
对象被引用,例如: b = a
对象被作为参数,传入到一个函数中,例如: func(a)
对象作为一个元素,存储在容器中,例如: list1 = [a,a]
导致引用计数-1的情况
对象的别名被显式销毁,例如: del a
对象的别名被赋予新的对象,例如: a = 24
一个对象离开它的作用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会)
对象所在的容器被销毁,或从容器中删除对象
查看一个对象的引用计数
import sys a = "hello world" sys.getrefcount(a)
可以查看a对象的引用计数,但是比正常计数大1,因为调用函数的时候传入a,这会让a的引用计数+1
循环引用导致内存泄露
引用计数的缺陷是循环引用的问题
import gc class ClassA(): def __init__(self): print("object born,id:%s"%str(hex(id(self)))) def f2(): while True: c1 = ClassA() c2 = ClassA() c1.t = c2 c2.t = c1 del c1 del c2 # 把python的gc关闭 gc.disable() f2()
执行f2(),进程占用的内存会不断增大。
创建了c1, c2后这两块内存的引用计数都是1,执行c1.t = c2和c2.t = c1后,这两块内存的引用计数变成2.
在del c1后,内存1的对象的引用计数变为1,由于不是为0,所以内存1的对象不会被销毁,所以内存2的对象的引用数依然是2,在del c2后,应用计数也是1,内存1的对象,内存2的对象的引用数都是1.
虽然他们两个对象都是可以被销毁的,但是由于循环引用,导致垃圾回收器不会回收它们,就导致内存泄漏.
垃圾回收
垃圾回收 = 垃圾检查 + 垃圾回收
有三种情况会触发垃圾回收:
调用gc.coolect()
当gc模块的计数器达到阀值的时候
程序退出的时候
gc模块
gc模块作用:解决循环引用的问题
常用函数:
gc.set_debug(flags)设置gc的debug日志。一般设置为gc.DEBUG_LEAK
gc.collect([generation]) 显式进行垃圾回收,可以输入参数,0表示只检查第一代的对象,1代表检查一,二代对象,2代表检查一,二,三代的对象。如果不传入参数,执行一个full collection,也就是等于2,返回不可达(unreachable objects)对象的数目
gc.get_threshold()获取的gc模块中自动执行垃圾回收的频率
gc.set_threshold(threshold0[, threshold1[, threshold2])设置自动执行垃圾回收的频率
gc.get_count()获取当前自动执行垃圾回收的计数器,返回一个长度为3的列表
Note:
gc模块唯一处理不了的是循环引用的类都有__del__方法,项目中要避免定义__del__方法。
class ClassA(): pass # def __del__(self): # print("object born,id:%s"%str(hex(id(self))))
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/41387.html
阅读 5148·2021-09-22 15:50
阅读 1833·2021-09-02 15:15
阅读 1135·2019-08-29 12:49
阅读 2506·2019-08-26 13:31
阅读 3421·2019-08-26 12:09
阅读 1180·2019-08-23 18:17
阅读 2672·2019-08-23 17:56
阅读 2903·2019-08-23 16:02