# 说点关于java与python的something:
# python中只有共有的和私有的,
# java中除此之外还有保护的,
# 封装:把函数和属性装到一个非全局的命名空间,
#类,就是封装而来,不过好像其实好像也是通过函数来实现的,
class A:
__N = 'aaa'
#首先从位置上看,它是静态变量,
#其次在类内部!!!定义的时候,前面有双_,说明是私有的,
#私有的第一反应可能是不能从外部直接访问,
def func(self):
print(self.__N) #在类内部访问是OK的,
a = A()
a.func()
# print(A.__N)
#报错:AttributeError: type object 'A' has no attribute '__N',说明私有的,确实不能直接访问,
print(A.__dict__)
#打印的字典中会有 '_A__N': 'aaa',这么一项,不过有点改变,
#也就是说,我们定义的私有变量,编译器会在前面加_A,即_类名,的形式,
print(A._A__N) #打印:aaa
#说明,上面我们关于编译器对私有变量的处理,说对了,
#不过虽然你可以这样访问私有变量,但是不要这么做,违背了私有变量出现的初衷,
#!!!!!其次此处的转换过程,一定要记住,后面此处可能掉进吭里,
#就是说,在类的内部,你的代码遇到__名字,会被自动转换为_类名__变量名
class B:
def __init__(self,name):
self.__name = name
def func(self):
print('in func : %s'%self.__name)
b = B('alex')
print(b.__dict__) #打印:{'_B__name': 'alex'},在类的对象中也这么打印,
# print(b.__name) #报错,这里是外部,你得在前面加_B,类的内部就会自动帮我们加_类名,所以可以直接__name打印,
print(b._B__name) #没问题,
b.func() #这里是类的内部,所以可以self.__name直接使用,而不必self._B__name
print('_B__name' in B.__dict__) #打印False,说明这里的name是对象属性,类中没有,
# 私有的方法
class C:
def __wahaha(self):
print('wahaha')
def ADCa(self):
self.__wahaha()
c = C()
# c._C__wahaha() #这里与私有变量的道理一样的,
# c.ADCa() #这里能直接调用,是因为你通过非私有方法,在类的内部调用了私有方法,所以OK,
class D:
def __func(self): # '_D__func'
print('in func')
class E(D):
def __init__(self):
pass
# self.__func() # 这里会报错,因为私有的,不会被继承,
e = E()
print('_D__func' in E.__dict__) #False
print('_D__func' in D.__dict__) #True
#☆☆☆☆☆,五颗星
class D:
def __init__(self):
self.__func()
def __func(self):
print('in D')
class E(D):
def __func(self):
print('in E')
e = E()
# 私有的名字,在类内使用的时候,就是会变形成_该类名__方法名
# 以此为例 :没有双下换线会先找E中的func
# 但是有了双下划线,会在调用这个名字的类D中直接找_D__func
class F:
pass
F.__name = 'alex' # 不是在创建私有属性
print(F.__name) #打印:alex,能直接打印,说明不是私有的,
print('_F__name' in F.__dict__) #False
print('__name' in F.__dict__) #True
# ps:编译器对私有变量的处理是反生在类的内部的,
# 上面这张图,可以体现@property和@setter的牵连,
# 在init方法中,变量不应该是被计算出来的,虽然在语法上没有不允许你这么做,还有个坏处,就是这么做写死了,若体重改变,bmi无法跟着改变,
# @property,函数属性化,之后你不能直接修改了,并且不能在类中出现同名的变量,
# @property 4颗星,将一个方法伪装成属性,调用的时候,不在需要(),如果加了()反而会报错,重要程度4颗星
# @name.setter 3颗星
# @name.deleter 1颗星
class Person:
def __init__(self,name,weight,height):
self.name = name
self.__height = height
self.__weight = weight
# self.bmi = 'haha' #这里会报错,原因如下,
# 在一个类加载的过程中,会先加载这个中的名字,包括被property装饰的
# 在实例化对象的时候,python解释器会先到类的空间里看看有没有这个被装饰的属性,
# 如果有就不能再在自己对象的空间中创建这个属性了
@property
def bmi(self):
return self.__weight / self.__height ** 2
p = Person('大哥',92,1.85)
print(p.bmi)
p._Person__weight = 900
print(p.bmi)
print('bmi' in Person.__dict__) #True,这个是方法,只是调用看起来像属性,所以它在类中,而不是对象中,
print('bmi' in p.__dict__) #False
#这是一个完整的@property,@setter,@deleter,的例子,
class Person:
def __init__(self,name):
self.__name = name # 私有的属性了
@property
def name(self):
return self.__name
@name.setter
def name(self,new_name):
if type(new_name) is str:
self.__name = new_name
else:
print('您提供的姓名数据类型不合法')
@name.deleter
def name(self):
del self.__name
p = Person('YQ')
print(p.name) #相当于返回值了,
p.name = 'WW' #相当于传参进去了,
print(p.name)
del p.name #执行了被@name.deleter装饰的函数,其它同理,
print(p.name)
# 通过下面两个报错,来讲讲这三个修饰
#1,AttributeError: 'function' object has no attribute 'deleter'
#2,NameError: name 'name' is not defined
#首先,这3个你可以理解为一套的,起关键作用的是@property,没有它后面2个都无从谈起!!!
#如果仅仅是没有@property,会报第一个错误,即找不到装饰器提供的方法,
#如果将整个@property修饰的方法删掉,后面会提示name未定义,
#这个例子,可以来个搞笑的解说:商品要做打折活动前,先涨价~~~~
class Goods:
def __init__(self,name,origin_price,discount):
self.name = name
self.__price = origin_price
self.__discount = discount
@property
def price(self):
return self.__price * self.__discount #获取的是折后价,
@price.setter
def price(self,new_price):
if type(new_price) is int or type(new_price) is float:
self.__price = new_price #修改的是商品的原价,
apple = Goods('apple',5,0.8)
print(apple.price)
apple.price = 8
print(apple.price)
# ps:
#将一些需要随着一部分属性的变化而变化的值的计算过程 从方法 伪装成属性
#比如折后价,就会随着原价(原价也会变)和打折力度的变化而变化,
#关于classmethod
class Goods:
__discount = 1
def __init__(self,name,origin_price):
self.name = name
self.__price = origin_price
@property
def price(self):
print('查看',self._Goods__discount)
return self._Goods__discount*self.__price
@price.setter
def price(self,new_discount):
Goods.__discount = new_discount #这种可以
Goods._Goods__discount = new_discount #这种也可以
print('设置后',self.__discount)
def func(self,num):
Goods._Goods__discount = num #这个函数的出现,最开始是想证明是不是@setter修饰器有使用限制,进而导致的错误,实际上并不是,
print(self._Goods__discount)
@property
def change_discount(self):
pass
@change_discount.setter #我想验证这两个混合到一起,混怎么样,结果'change_discount': 5,
@classmethod #结论:不能这样用.
def change_discount(cls, new_count):
cls.__discount = new_count
apple = Goods('apple',5)
banana = Goods('banana',8)
# Goods.change_discount = 5 #混合,不能用,
# apple.change_discount = 5 #混合,不能用,
# apple.price = 4 #可以,
# apple.func(5) #可以,
# Goods._Goods__discount = 222 #可以
print(apple.price)
print(banana.price)
print(apple.__dict__)
print(Goods.__dict__)
# 最新版,
# python中的类,除了可以在init中创建类对象的属性,
# 而且可以在init函数,比如类的其它函数中通过self.新的变量名 = xx的形式,添加一个新的属性,
# 所以在我们这里,我们通过self.__discount,相当于为类对象,添加了一个新的私有变量,仅仅对当前这个对象有效,
# 前面我总结的有些问题,只要你能唯一定位到类本身的静态变量,就可以改变他, 和@setter装饰器没关系,
# @classmethod的需求,是基于不需要实例化对象的情况下,并不是唯一的解决方案,不过被classmethod修饰的函数,有一个cls指针,直接指向类本身,可以直接访问类的变量,
# 而对于非@classmethod修饰的方法,你若想访问或者修改静态私有变量,你就得用类名._类名__静态变量名 = xx,的形式来修改,访问类似,\
# 在访问中还好,因为编译器找不到,就自动到类中去找;而对于修改,就是我们前面说的,会为类的对象添加一个新的,
# ps:
#这里有个要注意的地方,python对于你没有的变量,如果你直接修改,可能就直接帮你添加了,以至于不会报错,让你傻傻的不知道什么情况,
# 关于staticmethod
class Student:
def __init__(self,name):pass
@staticmethod
def login(a): # login方法没有默认参数self,它的使用可以理解为当普通函数那样使用,前面加个类名即可
user = input('user :')
if user == 'alex':
print('success')
else:
print('faild')
Student.login(1)
# 完全面向对象编程,比如你还没用户登录进入,即没有实例化对象的时候,但是又有登录的需求,就用静态方法,
# 当一个方法要使用对象的属性时 就是用普通的方法
# 当一个方法要使用类中的静态属性时 就是用类方法
# 当一个方法要既不使用对象的属性也不使用类中的静态属性时,就可以使用staticmethod静态方法
#上面是使用总结,建议你这样使用,但是如果你不这样来做,比如我想用对象方法来修改类属性,像上面那个例子,也可以,但是麻烦一些,并且可能出现一些不会报错的问题