面对对象的三大特性:继承,多态,封装。
函数和属性装到了一个非全局的命名空间----封装。
封装:
在类中,静态属性,方法,对象属性都可以变成私有的,只需要在这些名字前加上‘__’(双下划线)。
在类内,只要你的代码遇到‘__’(双下划线).名字,就会被python解释器自动转换_类名__名字。
class A:
__N = 'aaa' # 静态变量
def func(self):
print(A.__N) # 在类的内部使用正常 ‘aaa’
print(A.__dict__) # 会以这种形式存储到字典中:{'_A__N': 'aaa'}
# print(A.__N) # 直接在外部调用会报错
# print(A._A__N) # 所以可以使用这种方式调用,但是不能这样调用。
a = A()
a.func()
# 一个私有的名字,在存储的过程中仍然会出现在A.__dict__中,所以我们仍然可以调用到。
# python对其的名字进行了修改:_类名__名字。
# 只不过在类的外部调用: '_类名__名字'去使用。
# 在类的内部可以正常的使用名字调用。
私有的名字不能被子类继承:
class D:
def __func(self): # _D_func
print('in func')
class E(D):
def __init__(self):
self.__func() # 它会找 _E__func 这样的名字。 e = E() # 会报错,因为私有的名字不能被继承,
class D:
def __init__(self):
self.__func() # 在哪个类中定义的名字就会以固定方式存储: _D__func
def __func(self): # _D__func
print('in D')
class E(D):
def __func(self): # _E__func
print('in E')
e = E() # 私有的名字,在类内使用的时候,就是会变成: _该类名__方法名
# 以上为例:没有双下划线会先找E中的func
# 但是有了双下划线,会在调用这个名字的类D中直接找_D__func
面试题
变形只在类的内部发生:
class F:pass
F.__name = 'alex' # 并不是创建私有属性。
print(F.__name)
print(F.__dict__) # {'__name': 'alex'} 并不是私有属性
Java中的对比:
public 共有的 在类的内部可以使用,子类可以使用,外部可以使用 python中所有正常的名字。
prctect 保护的 在类的内部可以使用,子类可以使用,外部不可以使用, python中没有
private 私有的 只能在类的内部使用,子类和外部都不可以使用 python中的 __名字。(双下划线)
私有的用法:
1,当一个方法不想被子类继承的时候。
2,有些属性或者方法不希望从外部被调用,只想提供给内部的方法使用。
@property 能够将一个方法伪装成一个属性。将代码变得更美观。
从原来的对象的对象名.方法名(),变成了对象名.方法名。
class Person:
def __init__(self,name,weight,height):
self.name = name
self.__height = height
self.__weight = weight
@property
def bmi(self):
return self.__weight/self.__height**2
print(Person.__dict__)
p = Person('顾小白',70,1.75)
print(p.__dict__) #{'name': '顾小白', '_Person__weight': 70, '_Person__height': 1.75}
# 被property装饰的bmi仍然是一个方法,存在Person.__dict__
# 对象的.__dict__中不会存储这个属性。 # 在一个类加载的过程中,会先加载这个的名字,包括被property装饰的。
# 在实例化对象的时候,python解释器会先到类的空间里看看有没有这个被装饰的属性。
# 如果有就不能在自己的对象空间中创建这个属性了。
@property ---> func 将方法伪装成属性,只能看,不能对之操作。
@func.setter ---> func 对伪装的属性进行赋值的时候调用这个方法,一般情况用来修改属性。
@func.deleter ---> func 在执行del 对象.func的时候调用这个方法,一般情况用来删除属性,一般不用。
class Person:
def __init__(self,name):
self.__name = name # 私有属性
@property # 将方法装饰成属性使用。 现在 p.name 相当于 p.name()
def name(self):
return self.__name
@name.setter
def name(self,new_name):
if type(new_name) is str: # 类型不对无法替换,防止乱改。
self.__name = new_name # 将new_name 赋值给 self.__name 这一步改变了值。
else:
print('类型不对,无法替换...')
@name.deleter
def name(self):
del self.__name # 删除self.__name属性
p = Person('alex')
print(p.name) # alex
p.name = 'alex_sb'
print(p.name) # alex_sb
del p.name
print(p.name) # 报错:AttributeError: 'Person' object has no attribute '_Person__name'
@classmethod : 类方法,可以直接被类调用,不需要默认传对象参数,只需要传一个类参数就行了。
# 全场改动折扣,不能单个改动。
class Goods():
__discount = 0.8 # 静态属性
def __init__(self,name,origin_price):
self.name = name
self.__price = origin_price
@property
def price(self):
return self.__price * Goods.__discount
@classmethod
def change_discount(cls,new_discount):
cls.__discount = new_discount
Goods.change_discount(1) # 可以在实例化之前使用。
g = Goods('薯片',10)
print(g.price)
staticmethod:
当一个方法要使用对象的属性时,就是使用普通的方法。
当一个方法要使用类中的静态属性时,就是用类方法。
当一个方法要既不使用对象的属性也不使用类中的静态属性时,就可以使用 staticmethod 静态方法。
class Student:
def __init__(self,name):pass
@staticmethod
def login(): # login就是一个类中的静态方法,静态方法没有默认参数,就当成普通函数。
pass Student.login()