目录
一、类属性和实例属性
实例属性:在实例对象中定义的属性
类属性(静态属性):
在类对象(也就是类)中定义的属性,并且多个实例对象之间共享一份类属性。
类属性的使用方式:
类名.类属性
也可以使用 对象名.类属性
注意:
(1)、对于不可变数据类型来说: 对象名.静态属性名称=值 是给对象中添加属性名称,而不是进行修改。
(2)、对于可变数据类型来说:
如果对象是修改可变数据类型变量中的数据,都有影响。
如果是从新给可变数据类型变量赋值,这是给该对象添加属性。
练习1:定义一个课程类定义一个language不可变数据类型变量,分别使用类对象和实例对象调用类属性。
class Course:
language = 'chinese' # 类属性 或 静态属性
# 使用类属性
ret = Course.language
print(ret,id(ret))
# 使用对象调用
math = Course()
ret = math.language
print(ret,id(ret))
'''
输出:
chinese 34620840
chinese 34620840
'''
练习2:分别使用类对象和实例对象修改类属性的值。
class Course:
language = 'chinese' # 类属性 或 静态属性
# 使用类对象修改类属性值
Course.language = 'python'
python = Course()
print(python.language) # python
# 使用对象修改类属性的值
math = Course()
math.language = '阿拉伯数字'
print(math.language) # 阿拉伯数字
english = Course()
print(english.laguage) # python
练习3:使用实例对象修改可变数据类型的元素值
class Course:
lst = ['上午']
math = Course()
math.lst.append('lll') # 修改可迭代的,在对象就能修改类属性
print(math.lst) # ['上午', 'lll']
english = Course()
print(english.lst) # ['上午', 'lll']
练习4:使用实例对象重新给类属性进行赋值
class Course:
lst = ['上午']
math = Course()
math.lst = ['lll'] # 重新赋值,开辟新的内存空间
print(math.lst) # [lll]
二、封装特性
1.什么是封装
在日常中封装指的是将我们的物品包裹起来,不让看到其内部,具有保护的功能。
在程序设计中,封装(Encapsulation)是将类中的某些部分(某些属性或者方法)隐藏起来,
对象不能直接使用隐藏起来的属性或者方法,具有保护功能。
总结:隐藏对象的属性和方法实现细节,仅对外提供公共访问方式。
封装格式: __属性或者方法名称。(我们也叫做私有属性或者方法)
封装的目的:保护隐私。
Class People:
# 私有属性
__name = '老赵'
# 私有方法
def __eat():
print('正在吃饭...')
注意:私有属性和私有方法只能在类的内部使用。
2.私有属性
格式:__属性名=值
在类的外部不能使用(对象不能调用私有属性)
练习1:创建一个Person类在类的外部创建age属性,但是age可以为负数。
class People:
def __eat(self):
print('吃火锅')
per = People()
per.age = -10
print(per.age) # -10
练习2:使用get_xxx(),和set_xxx()方法来判断age属性的值是否为负数。
class Person:
def set_name(self, name):
self.name = name
def get_name(self):
return self.name
def set_age(self, age):
if age < 0 or age > 150:
print('这不是人吧!')
self.age =0
else:
self.age = age
def get_age(self):
return self.age
def __str__(self):
msg = '姓名:{}\t年龄:{}'.format(self.name, self.age)
return msg
# 解决负数赋值的问题
person = Person()
person.set_name('王吧')
person.set_age(-10)
per_name = person.get_name()
per_age = person.get_age()
print(per_name,per_age)
print(person)
'''
这不是人吧!
王吧 0
姓名:王吧 年龄:0
'''
# 但是还是可以通过对象添加属性将之前的值覆盖掉
person = Person()
person.name = '赵四'
person.age = -10
print(person) # 姓名:赵四 年龄:-10
练习3:使用私有属性来解决属性值覆盖的问题。
class Person:
def set_name(self, name):
self.__name = name
def get_name(self):
return self.__name
def set_age(self, age):
if age < 0 or age > 150:
print('这不是人吧!')
self.__age =0
else:
self.__age = age
def get_age(self):
return self.__age
def __str__(self):
msg = '姓名:{}\t年龄:{}'.format(self.__name, self.__age)
return msg
# 创建对象调用setter方法,给私有属性设置值
person = Person()
person.set_name('赵四')
person.set_age(10)
print(person) # 姓名:赵四 年龄:10
# 对象调用私有属性报错了
person = Person()
person.__name = 'zxb'
person.__age = 20
print(person) # AttributeError: 'Person' object has no attribute '_Person__name'
3.私有方法
私有方法:在方法前添加__ 例如 __send_message()
私有方法的作用:就是在开发的过程中保护核心代码。
在类的外部不能使用(对象不能调用私有方法)
class People:
def __eat(self):
print('吃火锅')
def play(self):
print('玩游戏')
self.__eat() # 在类内部调用
B = People()
# B.__eat() # 外部无法调用 AttributeError: 'People' object has no attribute '__eat'
B.play() # 内部可以调用 __eat()
'''
玩游戏
吃火锅
'''
应运场景:
class Call:
def __tell(self):
print('正在打电话....')
def tell_all(self, money):
if money > 0:
self.__tell()
else:
print('欠费了啊!')
phone = Call()
phone.tell_all(10) # 正在打电话....
phone.tell_all(0) # 欠费了啊!
4.私有化封装后的限制
| 类中 | 可以访问 |
|---|---|
| 类外/对象外 | 不可以访问 |
| 子类/子类对象 | 不可以访问 |
注意事项:
1.在python中实现的封装操作,不是通过权限限制而是通过改名(name mangling 改名策略)实现的,名字变了找不到而已。
2.可以使用 __dict__可以查看属性(包括私有属性)的值
在类的内部使用私有属性,python内部会自动进行转换成 _类名__属性名。
在类的外部不能给对象添加私有属性因为不能转换成_类名__属性名类型。
class Person:
def __init__(self,name,age):
self.name = name
self.__age = age
def get_age(self):
return self.__age
def set_age(self, age):
if age < 0 or age > 150:
print('这不是人吧!')
self.__age =0
else:
self.__age = age
def __str__(self):
msg = '姓名:{}\t年龄:{}'.format(self.name, self.__age)
return msg
person = Person('赵四',30)
print(person.__dict__) # 查看对象中的所有属性 {'name': '赵四', '_Person__age': 30}
# 在类中使用私有属性,python内部会自动转换成_类名__属性
person.__age = 20 # 无法覆盖
print(person.__dict__) # {'name': '赵四', '_Person__age': 30}
3.可以通过 对象名._类名__方法或类名._类名__方法名访问到(但禁止这么干)
可以使用 _类名__私有属性名来获取值。但是一般情况下不要使用,了解即可。
三、装饰器
1.装饰器函数的本质:一个闭包函数
2.装饰器函数的作用:
在不修改原函数及其调用方式的情况下对原函数功能进行扩展
3.语法糖
格式:@装饰器名称
4.创建带返回值的装饰器
5.创建传递参数的装饰器
6.装饰器的固定格式
7.解决被装饰函数不能查看信息bug
导入wraps
from functools import wraps
@wraps(func)
8.添加多个装饰器
格式:
@wrapper1
@wrapper2
def f():
执行原理:先执行距离函数近的装饰器@wrapper2
def wrapper(fun):
def inner(*args, **kwargs):
# 被装饰的函数开始执行的代码
ret = fun(*args, **kwargs) # 被装饰的函数
# 被装饰的函数最后执行的代码
return ret
return inner
def func(*args, **kwargs):
print('zzz')
练习1:创建一个计算函数执行花费时间的闭包函数。
import time
def fun():
for i in range(99999999):
pass
def timer(func):
def inner():
start = time.time()
func()
end = time.time()
print(end - start)
return inner
ret = timer(fun)
ret() # 2.1191210746765137
练习2:使用语法糖修改timer()装饰器
import time
def timer(func):
def inner():
start = time.time()
func()
end = time.time()
print(end - start)
return inner
@timer
def fun():
for i in range(99999999):
pass
fun() # 2.134122133255005
练习3:创建一个带返回值的装饰器
import time
def time1(fun):
def inner():
start = time.time()
ret = fun()
end = time.time()
print(end - start)
return ret
return inner
@time1 # func = time1(func)
def func():
for i in range(99999999):
pass
return 'ok'
ret = func()
print(ret)
'''
2.169124126434326
ok
'''
练习4:创建一个带参数的装饰器
def time1(fun):
def inner(s):
start = time.time()
ret = fun(s)
end = time.time()
print(end - start)
return ret
return inner
@time1 # func = time1(func)
def func(s):
for i in range(99999999):
pass
return s
ret = func('hello')
print(ret)
'''
2.14412260055542
hello
'''
练习5:定义一个可以传递任意参数的装饰器
import time
def time1(fun):
def inner(*args, **kwargs):
start = time.time()
fun(*args, **kwargs)
end = time.time()
print(end - start)
return inner
@time1 # func = time1(func)
def func(*args, **kwargs):
for i in range(99999999):
pass
print('传递过来的参数:',args,kwargs)
func('hello', age=10)
'''
传递过来的参数: ('hello',) {'age': 10}
2.160123586654663
'''
练习7:使用@wraps(func) 解决被装饰函数的信息不能查看问题。
import time
from functools import wraps
def time1(fun):
@wraps(fun)
def inner(*args, **kwargs):
start = time.time()
fun(*args, **kwargs)
end = time.time()
print(end - start)
# return ret
return inner
@time1 # func = time1(func)
def func(*args, **kwargs):
'''
这个函数不一般啊!
:param args:
:param kwargs:
:return:
'''
for i in range(99999999):
pass
print('传递过来的参数:',args,kwargs)
func('hello', age=10)
print(func.__name__)
print(func.__doc__) # 查看注释 加上装饰器后无法查看
'''
传递过来的参数: ('hello',) {'age': 10}
2.167124032974243
func
这个函数不一般啊!
:param args:
:param kwargs:
:return:
'''
练习8:给函数添加多个装饰器
def wrapper1(func):
def inner1():
print('wrapper1...start')
func()
print('wrapper1...end')
return inner1
def wrapper2(func):
def inner2():
print('wrapper2...start')
func()
print('wrapper2...end')
return inner2
@wrapper1
@wrapper2
# 从下往上执行
def fun():
print('fun....')
fun()
'''
执行顺序:
inner2 = wrapper2(fun)
inner1 = wrapper1(inner2)
fun() ===> inner1()
'''
'''
wrapper1...start
wrapper2...start
fun....
wrapper2...end
wrapper1...end
'''
四、@property装饰器
1、@property 内置装饰器函数,把一个方法调用方式变成属性调用方式。(将一个方法当成一个属性使用)。注意@property装饰器只能在面向对象中使用。
2、访问使用@property装饰器装饰的函数可以直接调用函数名
(会执行一段功能(函数)然后返回值)
3、@property 装饰器只能修饰不带参数的方法。
class P:
@property
def lll(self,age):
print(age)
p = P()
p.lll # 无法传递参数 TypeError: lll() missing 1 required positional argument: 'age'
4.@property装饰器修饰私有方法的getter和setter方法。和控制属性的访问权限-只读。
class Person:
def __init__(self, name, age):
self.__name = name
self.__age = age
@property
def name(self):
return self.__name
@property
def age(self):
return self.__age
@name.setter # @property修饰的方法名是什么这里就是什么.setter
def name(self, name):
if not name.isalpha():
print('不合法!')
return
self.__name = name
p = Person('zs', 11)
p.age = 23
print(p.age) # AttributeError: can't set attribute
练习:BMI指数计算
成人的BMI数值:
体质指数(BMI)=体重(kg)÷身高^2(m)
# BMI指数计算
class BMI:
def __init__(self, name, height, weight):
self.name = name
self.height = height
self.weight = weight
@property
def bmi(self):
num = self.weight / (self.height ** 2)
self.num = num
if num > 32:
self.bmi_num = '非常肥胖'
elif num >= 28 and num <= 32:
self.bmi_num = '肥胖'
elif num >= 24 and num < 28:
self.bmi_num = '过重'
elif num >= 18.5 and num < 24:
self.bmi_num = '正常'
else:
self.bmi_num = '过轻'
def __str__(self):
msg = '姓名:{},体重:{},身高:{},BMI值:{},身体状况:{}'.format(self.name, self.weight, self.height, self.num, self.bmi_num)
return msg
people = BMI('zxb', 1.75, 75)
people.bmi
print(people) # 姓名:zxb,体重:75,身高:1.75,BMI值:24.489795918367346,身体状况:过重