# 面向对象的三大特性
# 封装:将一些重要的数据和信息放到一个空间,比如函数
# 面向对象:
class A:
country = "China" # 静态属性也是一种封装
area = "深圳"
def __init__(self, name, age): # 这也是封装
self.name = name
self.age = age
a1 = A("小明", 22)
a2 = A("小花", 18)
print(a1.__dict__) # 对象属性的封装
print(a1.name) # 通过万能的 . 来调用
# 多态
# 一种事物有多种形态,比如水
# Python默认支持多态
# 鸭子类型
# 看着像鸭子,就是鸭子
# Python中约定俗成制定一些保持一致性的行为规范
class A:
def func1(self):
print("in A func1")
def func2(self):
print("in A func2")
class B:
def func1(self):
print("in B func1")
def func2(self):
print("in B func2")
# 将A类B类里面相似的一些功能命名成相同的名字,隐约制定一些标准
obj1 = A()
obj1.func1()
obj2 = B()
obj2.func1()
# 源码:
# str index
class Str_:
def index(self):
print("根据元素找索引")
class List_:
def index(self):
print("根据元素找索引")
class Tuple_:
def index(self):
print("根据元素找索引")
# 这三个类中都有 index,都是指索引,这样更加规范化
# 这三个类即互为鸭子
# 类的约束
# 目的是对类进行一些正确引导,约束,统一规范,满足正确的开发方式
class Alipay:
def pay(self, money):
print("此次消费%s元" % money)
class QQpay:
def pay(self, money):
print("此次消费%s元" % money)
a = Alipay()
a.pay(100) # 此次消费100元
q = QQpay()
q.pay(200) # 此次消费200元
# 统一支付功能
class Alipay:
def pay(self, money):
print("此次消费%s元" % money)
class QQpay:
def pay(self, money):
print("此次消费%s元" % money)
def pay(obj, money): # 设计一个接口
obj.pay(money)
a = Alipay()
a1 = Alipay()
q = QQpay()
pay(a, 100)
pay(q, 100)
pay(a1, 300)
# 假设要添加一个微信支付
class Alipay:
def pay(self, money):
print("此次消费%s元" % money)
class QQpay:
def pay(self, money):
print("此次消费%s元" % money)
class Wechat:
def fuqian(self, money): # 不规范
print("此次消费%s元" % money)
def pay(obj, money): # 这是一个隐藏的标准
obj.pay(money)
c = Wechat()
c.fuqian(300)
# 制定一个约束或标准
# 如果有父类,父类的方法只有一个pass
# 其实就是制定了一个规范,表明子类一定要有pay()方法
class A:
def pay(self, money):
pass
class Alipay(A):
def pay(self, money):
print("此次消费%s元" % money)
class QQpay(A):
def pay(self, money):
print("此次消费%s元" % money)
class Wechatpay(A):
def pay(self, money): # 不规范
print("此次消费%s元" % money)
def pay(obj, money): # 这是一个隐藏的标准
obj.pay(money)
c = Wechatpay()
c.pay(300)
# 在之前基础上
class A:
def pay(self, money):
pass
class Alipay(A):
def pay(self, money):
print("此次消费%s元" % money)
class QQpay(A):
def pay(self, money):
print("此次消费%s元" % money)
class Wechatpay(A):
def pay(self, money): # 不规范
print("此次消费%s元" % money)
class Unitypay(A):
def zhifu(self, money):
print("此次消费%s" % money)
def pay(obj, money): # 这是一个隐藏的标准
obj.pay(money)
u1 = Unitypay()
pay(u1, 100)
# 虽然没有这个方法,但是也没有报错,因为父类中有 pay()
# 侧面说明 A类不是强制性约束,为了起到决定性的作用,可以强制加一个约束
# 只要不按规则走就直接报错
# 两种解决方法:
# 1. 在父类写一个相同的方法,此方法主动抛出一个错误, 提示应该在子类写这个方法
class A:
def pay(self, money): # 如果子类没有定义这个方法,使用了父类的就报错
raise Exception("未定义pay方法")
class Alipay(A):
def pay(self, money):
print("此次消费%s元" % money)
class QQpay(A):
def pay(self, money):
print("此次消费%s元" % money)
class Wechatpay(A):
def pay(self, money): # 不规范
print("此次消费%s元" % money)
class Unitypay(A):
def zhifu(self, money):
print("此次消费%s" % money)
def pay(obj, money): # 这是一个隐藏的标准
obj.pay(money)
u1 = Unitypay()
pay(u1, 100)
# File "G:/.../123asda.py", line 203, in pay
# raise Exception("未定义pay方法")
# Exception: 未定义pay方法
# 解决方法二:
# 在父类引用元类的抽象方法
# 在实例化对象的时候就会报错
from abc import ABCMeta, abstractmethod
class A(metaclass=ABCMeta):
"""
抽象类,接口类,制定一个规范,强制执行
"""
@abstractmethod
def pay(self, money):
pass
class Alipay(A):
def pay(self, money):
print("此次消费%s元" % money)
class QQpay(A):
def pay(self, money):
print("此次消费%s元" % money)
class Wechatpay(A):
def pay(self, money): # 不规范
print("此次消费%s元" % money)
class Unitypay(A):
def zhifu(self, money):
print("此次消费%s" % money)
def pay(obj, money): # 这是一个隐藏的标准
obj.pay(money)
u1 = Unitypay()
pay(u1, 100)
# Traceback (most recent call last):
# File "G:/.../123asda.py", line 274, in <module>
# u1 = Unitypay()
# TypeError: Can't instantiate abstract class Unitypay with abstract methods pay
总结
约束. 其实就是⽗类对⼦类进⾏约束. ⼦类必须要写xxx⽅法. 在python中约束的⽅式和⽅法有两种:
1. 使⽤抽象类和抽象⽅法, 由于该⽅案来源是java和c,所以使⽤频率还是很少的
2. 使⽤⼈为抛出异常的⽅案. 并且尽量抛出的是NotImplementError
这样比较专业, ⽽且错误比较明确.(推荐)
# 类的私有成员
# Python的结构分析
# 类的结构
# 分为属性和方法
# 按照公有,私有对类进行划分
# 私有分为三部分;
class Boss:
name = "alex" # 公有静态属性 公有静态字段
__secretary = ["女1", "男2", "眼膜"] # 私有普通字段
def __init__(self, username, password):
self.username = username # 公有对象属性 公有普通字段
self.__password = password # 私有对象属性
def func(self):
# print(self.__secretary)
self.__func()
def __func(self): # 私有方法
print("经常做一些不可描述的事")
class Boss_son(Boss):
def func(self):
print(self.__secretary)
# 私有成员:私有静态属性,私有对象属性,私有方法
# 私有静态属性
# 访问它有三个方法:
# 1.类外部 Boss.name b1.name
print(Boss.name) # alex
print(Boss.__secretary) # 不能访问,报错
# 2.类内部 func()访问
b1 = Boss("alex", 123) #
b1.func() # ['女1', '男2', '眼膜']
# 3.子类
b2 = Boss_son("other", 123)
b2.func()
# 报错
b3 = Boss("abc", 123)
b3.__func() # 报错
b3.func() # 经常做一些不可描述的事
print(b3.__password) # 报错
b4 = Boss_son("asd", 123)
print(b4.__password) # 报错
# 因此私有方法只能在类内部访问,外部和派生类都不能
# 然而
class Boss:
name = "alex"
__secretary = ["女1", "男2", "眼膜"]
def __init__(self, username, password):
self.username = username
self.__password = password
def func(self):
self.__func()
def __func(self):
print("经常做一些不可描述的事")
b1 = Boss("asd", 1234)
print(Boss.__dict__)
# {'__module__': '__main__', 'name': 'alex',
# '_Boss__secretary': ['女1', '男2', '眼膜'],
# '__init__': <function Boss.__init__ at 0x000001BA3EA7AC80>,
# 'func': <function Boss.func at 0x000001BA3EA7AD90>,
# '_Boss__func': <function Boss.__func at 0x000001BA3EA7AD08>,
# '__dict__': <attribute '__dict__' of 'Boss' objects>,
# '__weakref__': <attribute '__weakref__' of 'Boss' objects>, '__doc__': None}
# print(Boss._Boss__secretary)
# 私有成员虽然可以在类外部或者派生类可以访问,但是不要这样做
# 类的方法
class A:
name = "barry"
def __init__(self, a, b): # 双下方法
self.a = a
self.b = b
def func(self): # 实例方法——可通过实例化对象调用的方法,普通方法
pass
@staticmethod # 静态方法,跟类和实例化对象没关系
def func1():
print(666)
@classmethod # 类方法
def func2(cls):
print(777)
@property # 将一个方法伪装成属性
def bim(self):
print(888)
# 类方法
# 类方法是通过类名直接调用的方法,类方法至少要有一个参数,第一个默认是cls
class A:
name = "barry"
def func(self): # 实例方法——可通过实例化对象调用的方法,普通方法
pass
@classmethod # 类方法
def func2(cls):
print(cls)
print(777)
a = A() # <class '__main__.A'>
# print(a)
# a.func()
print(A) # <class '__main__.A'>
A.func2() # 777
# 对象可以调用类方法,但是 cls 接收的不是对象的空间,而是类的空间
a.func2()
# <class '__main__.A'>
# 777
# 类方法在哪使用?
# 对类中的属性方法直接操作,与对象无关,这时需要使用类方法
# 原则上,类方法是将类本身作为对象进行操作的方法。假设有个方法
# 且这个方法在逻辑上采用类本身作为对象来调用更合理,那么这个方法就可以定义为类方法。
# 另外,如果需要继承,也可以定义为类方法。
# 如下场景:
# 假设我有一个学生类和一个班级类,想要实现的功能为:
# 执行班级人数增加的操作、获得班级的总人数;
# 学生类继承自班级类,每实例化一个学生,班级人数都能增加;
# 最后,我想定义一些学生,获得班级中的总人数。
#
# 思考:这个问题用类方法做比较合适,为什么?因为我实例化的是学生,
# 但是如果我从学生这一个实例中获得班级总人数,在逻辑上显然是不合理的。
# 同时,如果想要获得班级总人数,如果生成一个班级的实例也是没有必要的。
class ClassTest(object):
__num = 0
@classmethod
def addNum(cls):
cls.__num += 1
@classmethod
def getNum(cls):
return cls.__num
# 这里我用到魔术函数__new__,主要是为了在创建实例的时候调用人数累加的函数。
def __new__(self):
ClassTest.addNum()
return super(ClassTest, self).__new__(self)
class Student(ClassTest):
def __init__(self):
self.name = ''
a = Student()
b = Student()
print(ClassTest.getNum())
# 静态方法
# 相似功能,保持一致性,而类本来就是一种功能的划分
# 静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,
# 逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,
# 不会涉及到类中的属性和方法的操作。可以理解为,
# 静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。
class A:
name = "barry"
@staticmethod
def func():
print(666)
a1 = A
A.func() # 666
a1.func() # 666
import time
class TimeTest(object):
def __init__(self, hour, minute, second):
self.hour = hour
self.minute = minute
self.second = second
@staticmethod
def showTime():
return time.strftime("%H:%M:%S", time.localtime())
print(TimeTest.showTime()) # 12:23:37
t = TimeTest(2, 10, 10)
nowTime = t.showTime()
print(nowTime) # 12:23:37
# 类的特殊方法——property
# property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
# 测体质
class Bmi:
def __init__(self, kg, m):
self.kg = kg
self.m = m
def bmi_test(self):
return self.kg / (self.m**2)
b1 = Bmi(62, 1.7)
print(b1.bmi_test())
# 这个逻辑是没问题,但是本来 bmi 是名词,这里却当作动词,有点不太合理
class Bmi:
def __init__(self, kg, m):
self.kg = kg
self.m = m
@property
def bmi_test(self):
return self.kg / (self.m ** 2)
b1 = Bmi(62, 1.7)
# print(b1.bmi_test())
print(b1.bmi_test)
# property——将一个方法伪装成属性,逻辑没变,就只是看起来更合理
class A:
def __init__(self, username, password):
self.__username = username
self.__password = password
# @property
def name(self):
return self.__username
a1 = A("alex", 123)
# print(a1.username) # 报错
print(a1.name()) # alex
# 这样写不合理,name是名词,这里当作动词用
# 上面加了 @property,就可以这样调用
class A:
def __init__(self, username, password):
self.__username = username
self.__password = password
@property
def name(self):
return self.__username
a1 = A("alex", 123)
print(a1.name)
class A:
def __init__(self, username, password):
self.__username = username
self.__password = password
@property
def name(self):
return self.__username
# 下面的使用的前提是使用了 @property
@name.setter
def name(self, a):
print(a)
a1 = A("alex", 123)
print(a1.name)
a1.name = 666 # 这个赋值运算触发了@name.setter的 name()方法,如果没有赋值,比如上面的 a1.name 那就只是触发了 @property 的 name()
# 三个组合
class A:
def __init__(self, username, password):
self.__username = username
self.__password = password
@property
def name(self):
return self.__username
@name.setter
def name(self, a):
print(a)
@name.deleter
def name(self):
print(888)
a1 = A("alex", 123)
print(a1.name)
a1.name = 666 # 触发了@name.setter的name()方法
del a1.name # 触发了@name.deleter的name()方法
class A:
def __init__(self, username, password):
self.__username = username
self.__password = password
@property
def name(self):
return self.__username
@name.setter
def name(self, a):
if type(a) is str:
self.__username = a
else:
print("账号必须是字符串类型")
@name.deleter
def name(self):
print(888)
a1 = A("alex", 123)
a1.name = 666
# 触发了@name.setter的name()方法,然后进行判断得出结果
# 账号必须是字符串类型
a1.name = "abc"
# 注意这里 a1.name 触发了 @name.setter的name()
# 因此得出的结果是:把之前的 a1.name = "alex" 变为 "abc"
print(a1.name)
# 上一步因为 self.__username = a, name 已经是 "abc"
# 这里 a1.name 是触发了@property 的 name()
# 注意上面几个函数的函数名一定要一样!!!
class Supermarket:
def __init__(self, name, price, discount):
self.name = name
self.__price = price
self.__discount = discount
@property
def price(self):
return self.__price * self.__discount
@price.setter
def price(self, new_price):
self.__price = new_price
apple = Supermarket("苹果", 8.0, 0.95)
print(apple.price)
apple.price = 7.5 # 触发 @price.setter 的 name()
print(apple.price) # 触发 @property 的name()
# 为什么要用property
# 将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
# 由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
class Foo:
@property
def AAA(self):
print('get的时候运行我啊')
@AAA.setter
def AAA(self,value):
print('set的时候运行我啊')
@AAA.deleter
def AAA(self):
print('delete的时候运行我啊')
#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
# 或者:
class Foo:
def get_AAA(self):
print('get的时候运行我啊')
def set_AAA(self,value):
print('set的时候运行我啊')
def delete_AAA(self):
print('delete的时候运行我啊')
AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA