python学习笔记day7(面向对象进阶)

一.封装小知识点回顾:

  看以下代码打印内容:

class A:
    def __init__(self):
        self.__func()
    def __func(self):
        print('in A')
class B(A):
    def __func(self):
        print('in B')
b1 = B()
封装

  可以看出,当实例化一个b1对象时,会自动调用__init__方法,此时B类中并没有这个方法,因此会去他的父类A里边找,此时找到了__init__方法,并且他的执行代码为:self.__func(),当解释器遇到__func方法时,会将他自动加载到内存中,并将他改名为_A__func(),接下来程序就会调用这个方法,然后打印出 in A.

二.字段、方法、属性

  方法的分类:

 (1)普通方法属性一般由对象直接调用:

class A :
    name = 'alex'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def func(self):
        return 666
p1 = A("oldboy",1000)
print(p1.name)
print(p1.age)
print(p1.func())
对象调用方法属性
oldboy
1000
666
打印结果

 (2)类方法一般由类名直接调用:

class A :
    name = 'alex'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    @classmethod
    def func(self):
        return 666
print(A.func())
类方法

  注意此处的@classmethod就代表类方法,由类名直接调用即可,因此本题打印结果为:666

 (3)@property方法,是将类的属性封装成方法,虽然代码上看没什么提高,但她似乎更合情合理,具体代码如下:

  比如要求身高与体重的合集也就是我们通常说的bmi参数值:

class People:
    def __init__(self, name, weight, height):
        self.name=name
        self.__weight=weight
        self.__height=height
    @property
    def bmi(self):
        return self.__weight / self.__height**2
p1 = People('', 95,1.83)
print(p1.bmi)
案例:bmi值

 此处加了一个@property方法,我们就可以把bmi方法当做一个属性,通过一个对象来调用,因此打印结果为28.367523664486843

 接下来我们来看这样两个方法的用法:setter 和 deleter

class People:
    def __init__(self, name, age, sex):
        self.name = name
        self.__age = age
        self.__sex = sex
    @property
    def age(self):
        return self.__age
    @age.setter
    def age(self,new_age):
        if type(new_age) == int:
            self.__age = new_age
        else:
            print('请输入一个整数')
    @age.deleter
    def age(self):
        print(666)
p1 = People('', 28, '')
print(p1.name)
p1.name = ""
print(p1.name)
print(p1.age)   #触发了@property方法
p1.age = "1234" #触发了@age.setter方法
del p1.age      #触发了@age.deleter方法
setter和deleter方法

 如上当我们对age设定值的时候,就会自动调用@age.setter方法,判断是否符合age的设计格式,当我们删除age对象的时候就会自动调用@age.deleter方法,以下是打印结果:

 

 小案例练习:用上述方法实现一个超市商品打折的需求:苹果原价5元,打8折,后因双11将原价改为8元:

class Goods:
    def __init__(self,name,price,count):
        self.name = name
        self.__price = price
        self.__count = count
    @property
    def money(self):
        return self.__price * self.__count
    @money.setter
    def money(self,new_price):
        self.__price = new_price
app = Goods("apple",5,0.8)
print(app.money)
app.money = 8
print(app.money)
商品打折案例

 如上就实现了这样一个案例,其打印结果为:

 

(4).类方法:一般是由类名调用.有些情况,对于类内部的方法,无需对象直接调用,而类名直接调用即可。

class Goods:
    __discount = 0.8
    def __init__(self,name,origin_price):
        self.name = name
        self.__origin_price = origin_price
    @property
    def price(self):
        return self.__origin_price * Goods.__discount
    @classmethod
    def discount(cls,new_discount):
        Goods.__discount = new_discount

p1 = Goods('apple',5)
print(p1.price)
# p1.discount(0.85)
Goods.discount(0.85)
print(p1.price)
类方法示例

  打印结果为:

  

(5).静态方法:不需要传入对象,和类名,直接调用即可.用到了@staticmethod方法

class A:
    def __init__(self):
        pass
    @staticmethod
    def login(username,password):
        print(username,password)
A.login('alex',123)
静态方法示例

三.接口类和抽象类

(1).接口类和抽象类是一种规范,写代码时的一种规范,接下来我们通过代码一步步深入:

 版本1:设计两个类,qq和支付宝分别支付:

class Qqpay:
    def pay(self,money):
        print("您通过QQ支付%s"%money)
class Alipay:
    def pay(self,money):
        print("您通过支付宝支付%s"%money)
qq = Qqpay()
qq.pay(100)
ali = Alipay()
ali.pay(200)
版本一

 接下来我发现上述版本支付方式不统一,我想要一个版本统一的方法:

 版本2:归一化设计,规范支付方式:

class Qqpay:
    def pay(self,money):
        print("您通过QQ支付%s"%money)
class Alipay:
    def pay(self,money):
        print("您通过支付宝支付%s"%money)
def pay(obj,money):
    obj.pay(money)
qq = Qqpay()
ali = Alipay()
pay(qq,100)
pay(ali,200)
版本二

 以上虽然重新定义了一个方法规范了支付方式,但存在另一个问题,假设你的程序给别人使用,别人要重新开发一个微信支付的接口,而恰巧这个人就是野生程序员呢?他不会管你之前的功能,只会自己重新开发一个微信支付功能,程序这样设计:

 版本3:野生程序员-->开发微信支付功能:

class Qqpay:
    def pay(self,money):
        print("您通过QQ支付%s"%money)
class Alipay:
    def pay(self,money):
        print("您通过支付宝支付%s"%money)
class Weachat:
    def fuqian(self,money):
        print("您通过微信支付%s"%money)
def pay(obj,money):
    obj.fuqian(money)
qq = Qqpay()
ali = Alipay()
we = Weachat()
pay(qq,100)
pay(ali,200)
pay(we,300)
版本三

 上述程序虽然实现了开发一个微信支付功能但她改变了原来的两种支付方式,导致程序无法调用到原来的支付方式,因此这个版本不行。

 版本4:不能改变原来支付方式:

class Qqpay:
    def pay(self,money):
        print("您通过QQ支付%s"%money)
class Alipay:
    def pay(self,money):
        print("您通过支付宝支付%s"%money)
class Weachat:
    def pay(self,money):
        print("您通过微信支付%s"%money)
def pay(obj,money):
    obj.pay(money)
qq = Qqpay()
ali = Alipay()
we = Weachat()
pay(qq,100)
pay(ali,200)
pay(we,300)
版本四

 可以看出,这个代码虽然实现了不改变原来的支付方式,也实现了重新开发了一个支付功能,但还有一个问题就是:对于刚接手地方的程序员并不知道你之前的开发方式,这样不好。

 版本5:接口类抽象类

from abc import ABCMeta,abstractclassmethod
class Payrole(metaclass=ABCMeta):  # 抽象类或者接口类,制定规范,统一方法名。
    @abstractclassmethod
    def pay(self):
        pass
class Qqpay:
    def pay(self,money):
        print("您通过QQ支付%s"%money)
class Alipay:
    def pay(self,money):
        print("您通过支付宝支付%s"%money)
class Weachat:
    def pay(self,money):
        print("您通过微信支付%s"%money)
def pay(obj,money):
    obj.pay(money)
qq = Qqpay()
ali = Alipay()
we = Weachat()
pay(qq,100)
pay(ali,200)
pay(we,300)
最终版:接口类

 这样在代码的起始,我们用这个方法制定规范,统一了方法名,让后续的开发者一看便知,以上就是接口类的具体实现。

四.其它成员方法

   1.isinstance() 判断对象是否属于这个类,或者跟这个类血缘关系.

class C:
    pass
class B():
    pass
class A(B):
    pass
abj = A()
print(isinstance(abj,A))   #True
print(isinstance(abj,C))   #False
isinstance方法

  issubclass() 判断 第一类是第二个类的后代:

class C:
    pass
class B(C):
    pass
class A(B):
    pass
abj = A()
print(issubclass(A,B))   #True
print(isinstance(A,C))   #False
issubclass

 2.反射(重要)

  (1)用在类中:

 看这样一段代码,如何通过输入变量名role的值呢?以往的方法时这样

class A:
    role = 'Teacher'
    def func(self):
        print(666)
msg = input('>>>')  #输入role
print(A.role)        #teacher
print(A.__dict__[msg])  #teacher
通过输入变量获取值

 用反射应该怎样实现呢:

class A:
    role = 'Teacher'
    def func(self):
        print(666)
msg = input('>>>')  #输入role
print(getattr(A,msg)) #通过字符串去到类中获取相应的值  teacher
hasattr(A,msg)
print(hasattr(A,msg))  #通过字符串到类中判断是否存在此值  True
#通过字符串判断此值是否存在,存在则打印
if hasattr(A,msg):
    print(getattr(A,msg))
else:
    print("不存在此值")
# setattr()  #增加或者修改
setattr(A,"name","alex")  #增加属性
setattr(A,"role","student")  #修改原有属性
print(A.__dict__)  #查看属性
# delattr()  #删除属性
delattr(A,"role")
在类中的反射

 (2)用在对象中:

class A:
    role = "teacher"
    def __init__(self,name,age):
        self.name = name
        self.age = age

a = A("太白",18)
print(hasattr(a,"name"))  #判断是否存在 True
print(hasattr(a,"sex"))   #判断是否存在 False
print(getattr(a,"age"))   #得到属性值
setattr(a,"name","alex")  #修改属性值
print(getattr(a,"name"))   #得到属性值
在对象中的反射

  在python中,一切皆对象,凡是通过什么.什么调用的,都能用反射实现

 (3).用在模块中

import time
print(time.time())
print(getattr(time,"time")())
调用time模块
import oldboy
getattr(oldboy,"func")()
#方式一:
print(getattr(oldboy,"A"))
print(getattr(oldboy,"A").role)
print(getattr(oldboy,"A").func1)
# 方式二
print(getattr(getattr(oldboy,'A'),'role'))


#oldboy.py
def func():
    name = 'alexsb'
    print('此函数完成的是打印666功能')
class A:
    role = 'Teacher'
    def func1(self):
        print(666)
导入oldboy模块

   (4).用在当前模块中:

def login():
    print(666)
msg = input('>>>')
import sys
getattr(sys.modules[__name__],msg)()
用在当前模块

  (5).__len__方法的使用:

class A:
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3
    def __len__(self):
        return len(self.__dict__)
t = A()
print(len(t))
__len__方法

 上述在调用len(t)时,自动触发__len__方法,并调用,因此返回结果为3;

  (6).__hash__方法的使用:

class A:
    def __init__(self):
        self.a = 1
        self.b = 2
    def __hash__(self):
        return hash(self.a+self.b)

a = A()
print(hash(a))   #1+2
print(hash([1,2,3]))   #TypeError: unhashable type: 'list'
__hash__

 上述在调用hash(a)时,自动触发__hash__方法,因此返回结果1+2

 (7).__str__方法的使用:

class A:
    def __init__(self):
        self.a = 1
        self.b = 2
    def __str__(self):
        return "666"
a = A()
print("%s"%a)
__hash__

 __str__方法:当打印对象时自动触发__str__方法,因此打印结果为666

(8).__repr__方法的使用:

class A:
    def __init__(self):
        pass
    def __repr__(self):
        return "666"
a = A()
print(repr(a))  #666
print("%r"%a)  #666
__repr__

 当调用repr(a)方法时自动触发__repr__方法,因此打印结果为666

 (9).__call__方法的使用:

class A:
    def __init__(self):
        pass
    def __call__(self, *args, **kwargs):
        print("__call__")
obj = A()
obj()
__call__

 当实例化一个对象的时候,通过对象()的方式自动触发__call__方法

 (10).__eq__方法的使用:

class A:
    def __init__(self):
        self.a = 1
        self.b = 2
    def __eq__(self,obj):
        if self.a == obj.a and self.b == obj.b:
            return 666
a = A()
b = A()
print(a == b)
__eq__

 判断两个对象是否相等,执行a==b 自动触发__eq__方法

(11).__del__方法的使用:

class A:
    def __init__(self):
        self.a = 1
        self.b = 2
    def __del__(self):  # 析构方法
        print(222)
        return 666
obj1 = A()
__del__

  3.单例模式:一个类只能实例化一个对象

class A:
    __intance = None
    def __new__(cls, *args, **kwargs):
        if cls.__intance is None:
            obj = object.__new__(cls)
            cls.__intance = obj
        return cls.__intance
a = A()
b = A()
print(a)
print(b)
单例模式

 上述程序就实现了一个类只能实例化一个对象。

 其他方法:

class Foo:
    def __init__(self,name):
        self.name=name
    def __getitem__(self, item):
        print(self.__dict__[item])
    def __setitem__(self, key, value):
        print(key,value)
        self.__dict__[key]=value
    def __delitem__(self, key):
        print('del obj[key]时,我执行')
    #     self.__dict__.pop(key)
f1 = Foo('sb')
f1['name']  # 对对象进行字典式的查询操作 触发 __getitem__
f1['age'] = 18  # 对对象进行字典式的更改 触发 __setitem__
f1['age1']=19
del f1.age1
del f1['age']  # 对对象进行字典式的del删除 触发 __delitem__
f1['name']='alex'
print(f1.__dict__)
getitem方法

五.多态,鸭子类型

 Python默认就支持多态,自带多态,也叫鸭子类型。

from abc import ABCMeta,abstractmethod
class Payrole(metaclass=ABCMeta):  # 抽象类或者接口类,制定规范,统一方法名。
    @abstractmethod
    def pay(self): pass
class QQpay(Payrole):
    def pay(self,money):
        print('您已经通过qq支付了%s元' %money)
class Alipay(Payrole):
    def pay(self, money):
        print('您已经通过支付宝支付了%s元' % money)
    def func(self):
        pass
class Wechatpay(Payrole):
    def pay(self, money):
        print('您已经通过支付宝支付了%s元' % money)
def pay(obj,money):
    obj.pay(money)
示例一

    python 对于一些相似的方法,不用强制规定,都是约定俗成。

class Str:
    def index(self):
        print('通过元素找索引')
class List:
    def index(self):
        print('通过元素找索引')
class Tuple:
    def index(self):
        print('通过元素找索引')
示例二

六.封装

 1.知识点回顾:

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def func(self):
        print(self.name)
p1 = Person("太白",23)
p2 = Person("alex",38)
p1.func()
封装知识点回顾

    # 广义封装:封装到对象中的属性是一种封装。
 # 狭义封装:私有性封装。
   # 私有:私有静态字段(私有静态变量),私有普通字段(私有对象属性),私有方法

  (1).私有静态字段(私有静态变量)

class Person:
    country = "china" #公有静态字段(静态变量)
    __name = "oldboy" #私有静态字段(静态变量)
    __age = 18         #私有静态字段(静态变量)
    def func(self):
        return self.__name
print(Person.country)
# print(Person.__name)  在类的外部不能调用
p1 = Person()
# print(p1.__name)      #也不能通过实例化对象调用
print(Person.__dict__)
print(Person._Person__name)  #虽然可以但最好不要使用这种方法
print(p1.func())   #在类的内部可以访问
私有静态字段

 以上凡是带有__的私有静态属性,在类的外部均不可以调用的到,只能在类的内部进行访问。

 (2).派生类也不可访问父类的私有静态字段

class God:
    __kind = "黄皮肤"
class Person(God):
    country = "china" #公有静态字段(静态变量)
    __name = "oldboy" #私有静态字段(静态变量)
    __age = 18         #私有静态字段(静态变量)
    def func(self):
        return self.__kind
p1 = Person()
print(p1.func())   #不能访问
派生类也不可访问父类的私有静态字段

  # 私有静态字段:类内部可以访问,父类以及类的外部不能访问。
  # 只要类执行,将类的内容加载到内存时,发现有 __变量名 这样的Python解释器自动将__变量名
  # 转化成_类名__变量名

(3).# 私有普通字段:类内部可以访问,父类以及类的外部不能访问。
          # 只要类执行,将类的内容加载到内存时,发现有 __变量名 这样的Python解释器自动将__变量名
          # 转化成_类名__变量名

class Person:
    def __init__(self,name,age):
        self.name = name #公有普通字段
        self.__age = age #私有普通字段
    def func(self):
        return self.__age
p1 = Person("alex",1000)
print(p1.func())
私有普通字段的调用

 (4)私有成员在哪?

class Cipher_encryption:
    def __init__(self,username,password):
        self.username = username
        self.__password = self.__encryption(password)
    def __encryption(self,pwd):
        '''
        加密处理
        :param pwd:
        :return:
        '''
        return '%s hao123' % pwd
user1 = Cipher_encryption('alex', 'alexsb')
print(user1.username)
print(user1._Cipher_encryption__password)
print(user1.__dict__)
私有成员

 对于只想让类内部使用的成员,要设置成私有成员。

 

posted @ 2018-06-26 10:56  淋汐去水  阅读(91)  评论(0)    收藏  举报