python新手入门笔记(九)——面向对象的常见术语
私有属性和方法
私有化,在外部无法被访问,也无法被继承。
class Women:
def __init__(self, name):
self.name = name
# 私有属性,外界无法直接获取的属性
self.__age = 18
# self._类名__age 私有化属性实际样式
# 私有方法,不可被其他类继承
def __secret(self):
print(f'我的年龄是{self.__age}')
def get_age(self):
print(f'我的年龄是{self.__age}')
class Girl(Women):
def get_age(self):无法
print(f'我的年龄是{self.__age}')
if __name__ == '__main__':
xiaohong = Women('小红')
tiezhu = Girl('铁柱')
print(tiezhu.name)
tiezhu.get_age() # 'Girl' object has no attribute '_Girl__age',私有化方法无法继承,此处找不到私有化属性
print(xiaohong.name)
print(xiaohong._Women__age) # 常规写法不能用这个
# print(xiaohong.__age) # 'Women' object has no attribute '__age',外部不能直接访问私有化属性
xiaohong.get_age()
xiaohong._Women__secret() # 常规写法不能用这个
这里需要提的一点是:python的私有化是假的,它只是在被私有化的属性上加上了类名,即self._类名__age
,包括私有化方法也是一样的。但是我们常规写法并不能使用该方法,被定义了私有化,就应使用私有化应有的调用方法来实现,否则便失去了私有化的意义。
类方法和静态方法
类方法:使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法)。
静态方法:使用装饰器@staticmethod。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法。
class Person:
country = 'China'
def __init__(self,name):
self.name = name
# 类方法
@classmethod
def get_country(cls):
print(cls.country)
cls.country = 'UK'
print(cls.country)
@staticmethod
def info():
print('hahaha')
if __name__ == '__main__':
xm = Person('小埋')
print(xm.country)
xm.get_country()
附一个小栗子:
class Game:
class Game:
# 游戏最高分,类属性
top_score = 0
@staticmethod
def show_help():
print('玩法说明:让僵尸走进房间')
@classmethod
def show_top_score(cls):
print(f'游戏最高分{cls.top_score}')
def __init__(self,player_name):
self.player_name = player_name
def start_game(self):
print(f'{self.player_name}开始游戏')
# 直接使用类名修改类属性
Game.top_score = 777
if __name__ == '__main__':
Game.show_help()# 静态方法的通过类对象直接调用
Game.show_top_score()# 类方法的通过类对象直接调用
game = Game('xm') # 创建实例对象
game.start_game() # 实例对象的实例方法
game.show_top_score() # 实例对象调用类方法
注:实例方法也可调用静态方法。
单例、单例模式
1、一个类同一时间只能有一个实例,比如电脑只能打开一个回收站。
单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
class MusicPlayer:
flag = None # 记录第一次被创建的对象
flag_init = False#记录是否执行过init初始化操作
# 为对象分配内存空间、将对象的引用返回
#1 判断是否是空对象(空对象说明对象还没被创建)
def __new__(cls, *args, **kwargs):# 创建实例对象前先被__new__方法分配内存空间
if cls.flag is None:
#2 调用父类的方法,为第一个对象分配空间,并把对象本身赋值给类属性cls.flag
cls.flag = super().__new__(cls)
# 返回生成的对象的引用给init
return cls.flag
print()
#3 接收到对象、定义实例属性
def __init__(self):# 分配完内存空间后再从__new__方法中拿回实例对象
if not MusicPlayer.flag_init:
print('初始化播放器对象')
MusicPlayer.flag_init = True
player1 = MusicPlayer()
player2 = MusicPlayer()
print(id(player1))# 这两个实例对象的id是相同的
print(id(player2))
property属性
property是一个属性,而非函数,但它是用函数形式表示属性的。
当我们遇到需要进行一系列运算之后才能得到的属性时,就会用到property。
class Foo:
def __init__(self,cur_page):
self.cur_page = cur_page
self.page_items = 10
@property
def start(self):# 这是一个属性
# 有时候属性需要经过一系列运算得到,于是就产生了property
val = (self.cur_page-1) * self.page_items + 1
return val
@property
def end(self):
val = self.cur_page * self.page_items
return val
def run(self):
# 对于在别的函数中进行对property属性的调用,直接用self.所谓的函数名如self.start即可
print(f'目前是第{self.cur_page}页,显示第{self.start}到第{self.end}条数据')
if __name__ == '__main__':
foo_obj = Foo(2)
foo_obj.run()
对于在别的函数中进行对property属性的调用,直接用self.所谓的函数名如self.start即可
对于getter和setter的调用
property的另一种用法。和java的getter和setter的用法相似,都是对私有化属性的调用。
class Person:
def __init__(self,):
self.__name = '小埋'
self.__age = 18
def set_name(self,name):
self.__name = name
def get_name(self):
return self.__name
def set_age(self,age):
if 0 < age <= 120:
self.__age = age
else:
print('你没了')
def get_age(self):
return self.__age
# 这里需要注意的是,一个property只能有一对getter和setter,且getter在setter前面。
name = property(get_name,set_name)
age = property(get_age,set_age)
xm = Person()
print(xm.name,xm.age)
xm.name = 'xx'
xm.age = 17
print(xm.name,xm.age)
动态添加属性和方法
属性和方法不仅能在类中添加,还能在类外部向类添加。
class Man:
def __init__(self,name,age):
self.name = name
self.age = age
lbw = Man('lwb',18)
lbw.info = '赌怪'
print(lbw.info) #动态添加实例属性
Man.skill = '吃显示器' # 动态添加类属性
print(Man.skill)
# print(lbw.skill)
# 动态添加静态方法
@staticmethod
def say():
print('卡布奇诺')
Man.say = say # 动态添加静态方法
lbw.say()
# 动态添加类方法
@classmethod
def zhibo(cls):
print(cls.skill)
print('--------------------')
Man.zhibo = zhibo # 动态添加类方法
lbw.zhibo()
def run(self):
print(f'{self.name}说{self.info}')
lbw.run = run # 错误方法
lbw.run()
上述栗子表名可直接动态添加的属性和方法有——实例属性、类属性、静态方法、类方法。
实例方法不能直接添加,需要导入types
包。
import types
class Man:
def __init__(self,name,age):
self.name = name
self.age = age
def run(self):
print(f'{self.name}说{self.info}')
Man.run = run # 此方法错误,但能运行得到正确结果
lbw.run()
lbw.run = types.MethodType(run,lbw) # 给对象绑定实例方法
lbw.run()
利用同样给静态方法、类方法的方式动态添加实例方法的确是可行的,但实例方法是给实例对象用的,而不是给类用的,用类名.实例方法名 = 实例方法名
就相当于在类中添加了该方法,此时不仅特定的实例对象能用,还可以给别的实例对象用,这样的动态添加没有意义,还不如直接写进类中。
__ slots __
这算是动态添加的拓展。
__slots__属性限制了只有在__slots__序列中的属性才能被动态添加。
__slots__属性不会被继承
__slots__限制的是实例属性的添加
简单示例:
class A:
__slots__ = ('name','age')# 动态添加指定,其他的无效
class B(A):
pass
a = A()
a.name = '小埋'
a.age = 17
a.height = 170 # 这一步会报错
print(a.name,a.age,a.height)
b.height = 170
print(b.height)