1、构造函数
- 概念:构造函数包括__new__和__init__
- 构造函数,也被称为构造器,指的是当创建对象的时候,被自动调用的函数
- 注意:在Python中,以__xxx__方式命名的函数被称为魔术函数/魔术方法,这类函数在使用的时候无需手动调用,在进行某个操作的时候会自动调用
- (init)说明:先创建对象,然后使用直接赋值【动态绑定属性】方式给对象绑定属性,可以使用,但是代码比较繁杂,一般情况下,很多类倾向于将对象创建为有初始状态的,常规用法中,在类中可以定义一个函数,名称为__init__,该特殊的函数被称为构造函数,主要用于给对象的数据做出初始化
#语法:
'''
def __new__(cls,*args,**kwrags):
pass
def __init__(self):
pass
'''
#1、《介绍构造函数:__init__》(使对象初始化(绑定属性)变简单)
# class Man():
# __slots__ = ('house','money','power')
#
# zhang_san = Man()
# man.house = '无'
# man.money = '无'
# man.power = '无'
#
# li_si = Man()
# li_si.house = "有"
# li_si.money = '无'
# li_si.power = '无'
#可以看出上面定义对象的过程是比较繁琐的
class Man():
__slots__ = ('house','money','power')
def __init__(self,power):
self.house = '无'
self.money = '无'
self.power = power #如果需要创建对象的时候传参,则设置形参
print('__init__~~')
zhang_san = Man('无')
print('zhang_san(房子、钱、力量):',zhang_san.house,zhang_san.money,zhang_san.power)
li_si = Man('100000磅')
print('li_si(房子、钱、力量):',li_si.house,li_si.money,li_si.power)
'''
执行结果:
__init__~~
zhang_san(房子、钱、力量): 无 无 无
__init__~~
li_si(房子、钱、力量): 无 无 100000磅
总结:
a.当创建对象的时候,会自动调用__init__,(和其他类中函数一样,__init__中的self表示当前对象)
b.在__init__中动态绑定属性,如果需要创建对象的时候传参,则设置形参(且约定名字和属性名一致),如果不需要传参,则可以直接定义
c.如果需要给对象动态绑定属性,为了代码的可读性和后期的可维护性,一般将动态属性的绑定书写在__init__中!(对象初始化变简单)
d.当给__init__设置了和属性相关的参数之后,当创建对象的时候,一定要注意参数的匹配(设置形参的,必须在创建对象的时候传参)
'''
#2、《介绍构造函数:__new__》
# 【面试题】构造函数的工作原理
class Animal():
__slots__ = ('name','age')
def __new__(cls, *args, **kwargs):
print("new~~~~~~~")
# super().__new__(cls)表示创建对象
return super().__new__(cls)
def __init__(self,name,age):
print("init~~~",id(self))
self.name = name
self.age = age
a = Animal("大黄",4)
print(a) # <__main__.Animal object at 0x1043e6cd0>
print(id(a))
"""
总结:
a.真正意义上,构造函数包含__new__和__init__
b.在实际开发中,常用__init__,因为就算__new__在类中没有定义,当创建对象的时候仍然会自动调用
c.__new__:表示创建对象,该函数的返回值就是创建出来的对象
例如:a = Animal("大黄",4),a指向__new__的返回值
__init__:表示初始化对象,系统会自动将__new__的返回值【创建的对象】会自动传参给__init__中的self,
同时,将需要初始化的值传参给__init__中的其他参数,完成对象初始化的过程
d.构造函数中,当创建对象的时候,都是先调用__new__,然后才调用__init__
"""
2、类属性和实例属性
- 【面试题】简述类属性【类的字段】和实例属性【对象属性,对象的字段】的区别
- 1.定义位置不同:类属性直接定义在类中,实例属性定义在init中或在类的外面直接动态绑定定义
- 2.访问方式不同:类属性可以通过类名或对象访问,而实例属性只能通过对象访问
- 3.访问优先级不同:当类属性和实例属性重名时,通过对象访问,优先访问的是实例属性
- 4.在内存中出现的时机不同:类属性优先于实例属性出现在内存中,类属性随着类的加载而出现,实例属性是对象创建完毕之后才会出现
- 5.使用场景不同:类属性用于表示多个对象共享的数据,实例属性表示每个对象特有的数据
#1、类属性和实例属性的区别
# 1.1.定义位置:类属性直接定义在类中,实例属性定义在__init__中或在类的外面直接动态绑定定义
# 1.2.访问方式:类属性可以通过类名或对象访问,而实例属性只能通过对象访问
# 1.3.访问优先级不同:当类属性和实例属性重名时,通过对象访问,优先访问的是实例属性
# 1.4.在内存中出现的时机不同:类属性优先于实例属性出现在内存中,类属性随着类的加载而出现,实例属性是对象创建完毕之后才会出现
# 1.5.使用场景不同:类属性用于表示多个对象共享的数据,实例属性表示每个对象特有的数据
class A_603():
name = '603宿舍' #类属性
gender = '男'
def __init__(self,name,age):
self.name = name #实例属性
self.age = age #实例属性
zhang = A_603('zhang_san',18)
zhang.hobby = '购物' #实例属性
zhao = A_603('zhao_si',20)
print(f'验证1.2:{A_603.name},{zhang.age}') # 验证1.2:603宿舍,18
print(f'验证1.3:{A_603.name},{zhang.name}') # 验证1.3:603宿舍,zhang_san
print(f'验证1.5:{A_603.gender},{zhang.gender},{zhao.gender}') # 验证1.5:男,男,男
#注意1:通过对象名没法修改类属性,只有通过类名才能修改类属性(注意有时候虽然类属性和对象属性重名,但是要当做两个不同的属性对待)
zhang.gender = '女'
print(f'验证:{A_603.gender},{zhang.gender},{zhao.gender}') # 验证:男,女,男
zhang.gender = '男'
A_603.gender = '女'
print(f'验证:{A_603.gender},{zhang.gender},{zhao.gender}') # 验证:女,男,女
'''
(注意,上面的print中,_603.gender和zhao.gender访问的是类属性,zhang.gender访问自己的对象属性)
'''
#注意2:类属性和实例属性重名,修改实例属性,类属性不变,要修改类属性,需要单独修改!
print(f'zhang.name:{zhang.name},_603.name:{A_603.name}') # zhang.name:zhang_san,_603.name:603宿舍
zhang.name = 'zhang_si'
print(f'zhang.name:{zhang.name},_603.name:{A_603.name}') # zhang.name:zhang_si,_603.name:603宿舍
A_603.name = '666宿舍'
print(f'zhang.name:{zhang.name},_603.name:{A_603.name}') # zhang.name:zhang_si,_603.name:666宿舍
#结论1:不同对象访问同一个类属性,访问的是同一份内存空间,通过 类名.属性 = 值 修改类属性,一旦被修改所有对象访问到的类属性结果都会随着修改
#结论2:不同对象的同名的实例属性,在内存中是不同的内存空间,一个发生修改,对另一个没有任何影响
#结论3:类和对象属性虽然同名,但也是两个不同内存空间,一个发生改变,另一个不变
print('*'*30)
3、析构函数
构造函数 |
析构函数 |
类中new函数创建对象,类中init函数初始化对象 |
类中del函数在对象结束生命周期时调用,用来释放内存 |
#1、析构函数
class Person():
def __init__(self):
print("构造函数被调用了")
def __del__(self):
print("析构函数被调用了~~~~~")
# 1.当对象被定义为全局变量,当程序结束之后,__del__会被自动调用 (注意,测试下面代码时,其他代码要注释掉,否则‘析构函数’在代码结束执行)
# print("start")
# p1 = Person()
# print("over")
"""
执行结果:
start
构造函数被调用了
over
析构函数被调用了~~~~~
"""
print('*'*30)
# 2.当对象被定义为局部变量,当函数调用完毕,__del__会被自动调用
def func():
p2 = Person()
print("start")
func()
print("over")
"""
执行结果:
start
构造函数被调用了
析构函数被调用了~~~~~
over
"""
print('*'*30)
# 3.当对象被del删除,则__del__会被自动调用
print("start")
p3 = Person()
del p3
print("over")
"""
执行结果:
start
构造函数被调用了
析构函数被调用了~~~~~
over
"""