yiruia的个人博客分享 http://blog.sciencenet.cn/u/yiruia

博文

Day25 面向对象的多态、封装、property、staticmethod、classmethod

已有 1638 次阅读 2020-3-23 12:56 |系统分类:科研笔记

一、多态

class Alipay():
    def pay(self,money):
        print('已经使用支付宝支付了%s' % money)
class Applepay():
    def fuqian(self,money):
        print('已经使用Applepay支付了%s' % money)
def pay(pay_obj,money): # 统一支付入口 归一化设计
    pay_obj.pay(money)
pay()
  • 多态:一类事物有多种形态,例如Animal有人狗猪等多种形态

  • 多态性:是指在不考虑实例类型的情况下使用实例

  • python 天生支持多态,动态强类型的语言,崇尚鸭子类型

  • 鸭子类型:不崇尚根据继承所得来的相似,只是根据自己实现我自己的代码就可以,如果两个类刚好相似,并不产生父类的子类的兄弟关系,而是鸭子类型

  • index tuple这种相似是根据自己写代码的时候约束的,而不是通过父类约束的

        优点:松耦合,每个相似的类之间都没有影响

        缺点:太随意了,只能靠自觉

# list tuple
class List:
    def __len__(self):pass
class Tuple:
    def __len__(self):pass
def len(l_t):
    return l_t.__len__()
l = List()
print(len(l))

python中有鸭子类型,接口类和抽象类在python中的应用点并不是非常必要的


二、封装

广义上面向对象的封装:代码的保护,面向对象的思想本身就是一种封装,之让自己的对象能调用自己类中的方法。

侠义的封装:面向对象的三大特性之一,属性和方法都藏起来,不让人看到。


class Person:
    __key = 123 # 私有静态属性
    def __init__(self,name,passwd):
        self.name = name
        self.__passwd = passwd #私有属性
    def get_pwd(self):
        print(self.__dict__)
        return self.__passwd # 只要在类的内部使用私有属性,就会自动的带上_类名
    def login(self):
        self.__get_pwd()
alex = Person('alex','alex3714')
# print(alex.__passwd) # 不想让别人看到密码
print(alex.__dict__)
print(alex._Person__passwd) # _类名__属性名,可以调到密码,不能这样用,在代码级别就变得比较私有了
print(alex.get_pwd())
alex.__high = 1
print(alex.get_pwd())
print(alex.__high) # 在类的外部定义私有属性,不顶用,可以直接调用

'''
{'name': 'alex', '_Person__passwd': 'alex3714'}
alex3714
{'name': 'alex', '_Person__passwd': 'alex3714'}
alex3714
{'name': 'alex', '_Person__passwd': 'alex3714', '__high': 1}
alex3714
1
'''

所有的私有 都是在变量的左边加上双下划线

所有的私有 都不能在外部使用,即使知道了投机倒把的方法,也不能在类的外部使用

    对象的私有属性

    类中的私有方法

    类中的静态属性

图片.png


class A(object):
    a = 0
    name = None
    b = 1
    def __init__(self,name):
        self.a = 2
        self.b = 3
        self.name = name
    def test(self):
        print ('a normal func.')
class B(A):
    def test_B(self):
        print ('func named test_B')
obj = A('Tom')
obj1 = B('Jerry')
print (A.__dict__)
print (obj.__dict__)
print (obj.__dict__['name'])
print (B.__dict__)
print (obj1.__dict__)
#执行结果如下

{'__module__': '__main__', 'a': 0, 'name': None, 'b': 1, '__init__': <function A.__init__ at 0x00000200C2D61840>, 'test': <function A.test at 0x00000200C2D618C8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

{'a': 2, 'b': 3, 'name': 'Tom'}

Tom #可以通过键来获取对象__dict__属性中的值
{'__module__': '__main__', 'test_B': <function B.test_B at 0x00000200C2D61950>, '__doc__': None}

{'a': 2, 'b': 3, 'name': 'Jerry'}

在python中只要有__名字,就把这个名字私有化了,就不能从类的外部直接调用了。

静态属性、方法、对象属性都可以私有化,这种私有化只是从代码级别做了变形,并没有真的约束

变形机制:__类名,__名字,在类外用不能调用,在类的内部直接__名字调用

class Room:
    def __init__(self,name,length,width):
        self.name = name
        self.__length = length
        self.__width = width
    def get_name(self):
        return self.__name
    def set_name(self,newName):
        if type(newName) is str and newName.isdigit() == False:
            self.__name = newName
        else:
            print('不合法的姓名')

    def area(self):
       return self.__length * self.__width
jin = Room('金老板',2,1)
print(jin.area())
jin.set_name('2')
print(jin.name)

'''
2
不合法的姓名
金老板
'''
# 假设父类的私有属性 能被子类调用吗?不能
class Foo:
    __key = '123' # 变形成了_Foo__key
class Son(Foo):
    print(Foo.__key) # 发生了变形,变成了_Son__key
'''
print(Foo.__key)
AttributeError: type object 'Foo' has no attribute '_Son__key'
'''

会用到私有这个概念的场景:

  1. 隐藏起来一个属性,不想让类的外部调用

  2. 我想保护这个属性,不想让属性随意被改变

  3. 我先给保护这个属性,不想子类继承

三、只在面向对象中使用的内置装饰器函数

property

from math import pi
class Circle:
    def __init__(self,r):
        self.r = r
    def perimeter(self):
        return 2*pi*self.r

    def area(self):
        return pi*self.r**2
c1 = Circle(5)
print(c1.area())
print(c1.perimeter())

使用property改进:

from math import pi
class Circle:
    def __init__(self,r):
        self.r = r
    @property
    def perimeter(self):
        return 2*pi*self.r

    @property
    def area(self):
        return pi*self.r**2
c1 = Circle(5)
print(c1.area) # 借助@property将方法伪装成一个属性,调用方法的时候需要加括号,调用属性的不用
print(c1.perimeter)

借助property将方法伪装成一个属性,调用方法的时候需要加括号,调用属性的时候不用property


class Person:
    def __init__(self,name):
        self.__name=name
    def name(self):
        return self.__name + 'sb'
tiger = Person('泰哥')
print(tiger.name())
tiger.name = '全班'
print(tiger.name) # 无法更改
'''
泰哥sb
全班
'''

改进

class Person:
    def __init__(self,name):
        self.__name=name
    @property
    def name(self):
        return self.__name + 'sb'
    @name.setter
    def name(self,new_name):
        self.__name = new_name
tiger = Person('泰哥')
print(tiger.name)
tiger.name = '全班'
print(tiger.name)
'''
泰哥sb
全班sb
'''
class Goods:
    discount = 0.5
    def __init__(self,name,price):
        self.name = name
        self.__price = price
    @property
    def price(self):
        return self.__price*Goods.discount
apple = Goods('苹果',5)
print(apple.price) # 2.5
# 属性 查看 修改 删除
class Person:
    def __init__(self,name):
        self.__name=name
    @property
    def name(self):
        return self.__name
    @name.deleter #deleter只是和del互相关联
    def name(self):
        del self.__name
    @name.setter  
    def name(self,new_name):
        self.__name = new_name
brother2 = Person('二哥')
print(brother2.name)
del brother2.name
print(brother2.name)

classmethod

class Goods:
    __discount = 0.5
    def __init__(self,name,price):
        self.name = name
        self.__price = price
    @property
    def price(self):
        return self.__price*Goods.__discount
    @classmethod #把一个方法编程一个类中的方法,这个方法就直接可以被类调用,不需要依托任何对象,即不实例化对象仍然可以操作值
    def change_discount(cls,new_discount): #修改折扣
        cls.__discount = new_discount
apple = Goods('苹果',5)
print(apple.price)
apple.change_discount(0.8)
print(apple.price)

'''
2.5
4.0
'''

classmethod,把一个方法编程到一个类中的方法,这个方法就可以直接被类调用,不需要依托任何对象,即,不需要实例化对象仍然可以使用。当一个方法的操作只涉及静态属性的时候就应该使用classmethod来修饰这个方法。

staticmethod

class Login:
    def __init__(self,name,password):
        self.name = name
        self.pwd = password
    def login(self):pass

    @staticmethod
    def get_usr_pwd(): # 静态方法
        usr = input('用户名:')
        pwd = input('密码:')
        Login(usr,pwd)
Login.get_usr_pwd()

在完全面向对象的程序中,如果一个函数既和对象没有关系,也和类没有关系,那么就用staticmethod函数将这个函数编程成一个静态方法。

python staticmethod 返回函数的静态方法。

该方法不强制要求传递参数,如下声明一个静态方法:

class C(object):
    @staticmethod
    def f(arg1, arg2, ...):
        ...

以上实例声明了静态方法 f,从而可以实现实例化使用 C().f(),当然也可以不实例化调用该方法 C.f()

函数语法

staticmethod(function)

参数说明:

实例

#!/usr/bin/python# -*- coding: UTF-8 -*- class C(object):    @staticmethod    def f():        print('runoob'); C.f();          # 静态方法无需实例化cobj = C()cobj.f()        # 也可以实例化后调用

以上实例输出结果为:

runoob
runoob
  • 类方法和静态方法都是类调用的

  • 对象可以调用类方法和静态方法吗?可以,一般情况下,推荐用类名调用

  • 类方法有一个默认参数,cls代表这个类

静态方法没有默认的参数,就像函数一样,不必传self参数。

四、反射

反射是个很重要的概念,它可以把字符串映射到实例的变量或者实例的方法,然后可以取执行调用、修改等操作。它有四个重要的方法:

  • getattr 获取指定字符串名称的对象属性

  • setattr 为对象设置一个对象

  • hasattr判断对象是否有对应的对象(字符串)

  • delattr 删除指定属性

attr是属性英文的前几个字母,属性指的是类中类变量、实例变量和方法。但是注意不能是私有的,如果你的变量是以'__'开头的,那将无法获取。

反射常常用在动态加载模块的场景中


class Teacher:
    dic = {
        '查看学生信息':'', '查看讲师信息':''
    }
    def show_student(self):
        print('show_student')
    def show_teacher(self):
        print('show_teacher')

    @classmethod
    def func(cls):
        print('hahaha')

# hasattr getattr delattr
ret = getattr(Teacher,'dic')  # Teacher.dic #类.属性
print(ret)
ret2 = getattr(Teacher,'func') #类.方法 Teacher.func
print(ret2())
# menu = Teacher.dic
# for k in menu:
#     print(k)
if hasattr(Teacher,'dic4'): # hasattr和getattr经常配套使用
    ret = getattr(Teacher,'dic4')
    
'''
{'查看学生信息': '', '查看讲师信息': ''}
hahaha
None
'''
class Teacher:
    dic = {
        '查看学生信息':'show_student', '查看讲师信息':'show_teacher'
    }
    def show_student(self):
        print('show_student')
    def show_teacher(self):
        print('show_teacher')

    @classmethod
    def func(cls):
        print('hahaha')
alex = Teacher()
# alex.show_student()
func = getattr(alex,'show_student')
func()
'''
show_student
'''
class Teacher:
    dic = {
        '查看学生信息':'show_student', '查看讲师信息':'show_teacher'
    }
    def show_student(self):
        print('show_student')
    def show_teacher(self):
        print('show_teacher')

    @classmethod
    def func(cls):
        print('hahaha')
alex = Teacher()
for k in Teacher.dic:
    print(k)
key = input('请输入需求:')
print(Teacher.dic[key])
if hasattr(alex,Teacher.dic[key]):
    func = getattr(alex,Teacher.dic[key])
    func()
'''
查看学生信息
查看讲师信息
请输入需求:查看学生信息
show_student
show_student
'''




https://blog.sciencenet.cn/blog-3405644-1224884.html

上一篇:Day23 面向对象的继承
下一篇:Day26 数据分析
收藏 IP: 223.91.42.*| 热度|

0

该博文允许注册用户评论 请点击登录 评论 (0 个评论)

数据加载中...

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-4-24 07:48

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部