__del__方法(析构函数)和垃圾回收机制
__del__方法称为“析构方法”,用于实现对象被销毁时所需的操作。系统会自动提供__del__方法,一般不需要自定义析构方法。
Python 中当对象被变量调用时引用计数+1,当对象没有被引用时引用计数为0,由垃圾回收器自动调用__del__方法。也可以通过del 语句删除对象,从而保证调用__del__方法。
__call__方法和可调用对象
定义了__call__方法的对象,称为“可调用对象”,即该对象可以像函数一样被调用。函数通过函数名()调用函数,可调用对象和函数一样也是对象,可调用对象()可调用__call__方法,即该对象可以像函数一样被调用。
class SalaryAccount:
'''工资计算类'''
def __call__(self, salary):
yearSalary = salary * 12
daySalary = salary // 22.5
hourSalary = daySalary // 8
return dict(monthSalary=salary, yearSalary=yearSalary, daySalary=daySalary, hourSalary=hourSalary)
s = SalaryAccount()
print(s(5000)) # 可以像调用函数一样调用对象的__call__方法
方法没有重载
在其他语言中,可以定义多个重名的方法,只要保证方法签名唯一即可。方法签名包含3个部分:方法名、参数数量、参数类型。
但Python 中,方法的的参数没有声明类型(调用时确定参数的类型),参数的数量也可以由可变参数控制。因此,Python 中是没有方法的重载。如果在类结构体中定义了多个重名的方法,只有最后一个方法会生效,前面的同名方法将被覆盖。
总结:不要使用重名的方法!Python 中方法没有重载。
class Person:
def say_hi(self):
print("hello")
def say_hi(self, name):
print("{0},hello".format(name))
p1 = Person()
# p1.say_hi() # 同名方法前者被覆盖,不带参时报错
p1.say_hi("python")
方法的动态性
Python 是动态语言,可以动态的为类添加新的方法,或者动态的修改类的已有的方法
# 定义Preson类
class Person:
def learning(self):
print("hello,python")
# 与类无关的函数
def play_game(one):
print("{0}玩游戏".format(one))
def learn(one):
print('{0}learn coding to print "hello,python"'.format(one))
p = Person() # 新建实例对象
Person.play = play_game # 动态语言,引用外部函数对类添加新的方法
p.play() # 内部调用原理,Person.play(p)传入的参数为实例对象
p.learning() # 未更改前的类方法
Person.learning = learn # 动态语言,用外部函数更改类的方法
p.learning() # 更改后的类方法
运行结果:
<main.Person object at 0x0000016B7B92D3C8>玩游戏
hello,python
<main.Person object at 0x0000016B7B92D3C8>learn coding to print “hello,python”
私有属性和私有方法(实现对外封装)
Python 对于类的成员没有严格的访问控制限制,这与其他面向对象语言有区别。关于私有属性和私有方法,有如下要点:
- 通常约定,两个下划线"__"开头的属性是私有的(private),其他为公共的(public)。
- 类内部可以访问私有属性(方法)
- 类外部不能直接访问私有属性(方法)
- 类外部可以通过“_类名__私有属性(方法)名”访问私有属性(方法)
【注】方法本质上也是属性!只不过是可以通过()执行而已。所以,此处讲的私有属性和公有属性,也适用于私有方法和公有方法。
class Students:
__subject = "python" # 私有类属性通过dir,可以查到存储格式为_Students__subject
def __init__(self, name, age):
self.name = name
self.__age = age # 私有实例属性
def say_subject(self):
print("我的正在学习:", Students.__subject) # 类内部可以直接访问私有属性
print(self.name, "的年龄是:", self.__age)
self.__work() # 类内部可以直接访问私有方法
def __work(self): # 私有实例方法通过dir,可以查到_Employee__work
print("终身学习,财务自由!")
p1 = Students("张三", 25)
print(p1.name) # 公共属性可直接调用
print(dir(p1)) # dir(obj)可以获得对象的所有属性、方法;obj.__dict__ 自定义对象字典展示
print('*' * 40)
p1.say_subject()
print('*' * 40)
print(p1._Students__age) # 通过这种方式可以直接访问到私有属性。通过dir可以查到属性:_Students__age
p1._Students__work() # 通过这种方式可以直接访问到私有方法。通过dir可以查到方法性:_Students__work
print(Students._Students__subject) # 调用类属性格式相似,类属性属于类,不属于实例
# print(p1.__age) #直接访问私有属性,报错
# p1.__work() #直接访问私有方法,报错
运行结果:
张三
[’_Students__age’, ‘_Students__subject’, ‘_Students__work’, ‘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’, ‘name’, ‘say_subject’]
我的正在学习: python
张三 的年龄是: 25
终身学习,财务自由!
25
终身学习,财务自由!
python
【总结】:
属性和方法命名总结:方法和属性都遵循以下规则。
· _xxx:保护成员,不能用“from module import * ”导入,只有类对象和子类对象能访问这些成员。
· __xxx__:系统定义的特殊成员
· __xxx: 类中的私有成员,只有类对象自己能访问,子类对象也不能访问。(但在类外部可以通过“对象名. _类名__xxx”这种特殊方式访问。Python 不存在严格意义的私有成员)
@property 装饰器
@property 可以将一个方法的调用方式变成“属性调用”。
class Employee:
@property
def salary(self):
print("装饰器,将方法转换成属性调用")
return 30000
emp1 = Employee()
print(emp1.salary) # 在方法前添加装饰器@property,在外部调用方法时用属性方法调用,方法名后不用跟()
print('*'*40)
print(type(emp1.salary)) # <class 'int'>,是salary方法返回值的type
# emp1.salary =1000 # 修改报错,@property修饰的属性,在没有加setter方法的情况下,为只读属性。
运行结果:
装饰器,将方法转换成属性调用
30000
装饰器,将方法转换成属性调用
<class ‘int’>
# get与set的使用
class Employee:
def __init__(self, name, salary):
self.__name = name
self.__salary = salary # 避免外部可直接对类属性读取和赋值,私有化类属性
def get_salary(self):
print("月薪为{0},年薪为{1}".format(self.__salary, (12 * self.__salary))) # 类内部可直接调用类私有属性
return self.__salary
def set_salary(self, salary): # 条件判断,避免用户端输入错误值
if (0 < salary < 50000):
self.__salary = salary
print("已成功写入")
else:
print("薪水录入错误!只能在0-50000 之间")
emp1 = Employee("张三", 5000)
print(emp1.get_salary()) # 读取
emp1.set_salary(-3000) # 写入
print(emp1.get_salary()) # 确认是否写入,写入错误时不更改属性值
运行结果:
月薪为5000,年薪为60000
5000
薪水录入错误!只能在0-50000 之间
月薪为5000,年薪为60000
5000
对于某一个公共属性,可以直接通过:类.属性 = 值,来读操作/写操作。@property 主要用于处理属性的读操作,相当于get方法;@属性.setter主要用于处理属性的写操作,相当于set方法。
class Employee:
def __init__(self, name, salary):
self.__name = name
self.__salary = salary # 避免外部可直接对类属性读取和赋值,私有化类属性
# 避免客户端对类属性输入错误值,使用 @property读取, @属性.setter 可带条件写入
@property # 相当于salary属性的getter方法
def salary(self):
print("月薪为{0},年薪为{1}".format(self.__salary, (12 * self.__salary))) # 类内部可直接调用类私有属性
return self.__salary
@salary.setter # 相当于salary属性的setter方法
def salary(self, salary): # 条件判断,避免用户端输入错误值
if (0 < salary < 50000):
self.__salary = salary
print("已成功写入")
else:
print("薪水录入错误!只能在0-50000 之间")
emp1 = Employee("李四", 6000)
print(emp1.salary) # 读取时调用,@property下方法,不用带()。
emp1.salary = 20000 # 写入时调用,@salary.setter下方法,不用带()。
print(emp1.salary) # 确认是否写入
运行结果:
月薪为6000,年薪为72000
6000
已成功写入
月薪为20000,年薪为240000
20000