封装与扩展性

6.5 封装与扩展性

封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。 

#类的设计者
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积
        return self.__width * self.__length
#使用者
>>> r1=Room('卧室','egon',20,20,20)
>>> r1.tell_area() #使用者调用接口tell_area
400
#类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了
        return self.__width * self.__length * self.__high

对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能

>>> r1.tell_area()
8000

回到顶部

七 静态方法和类方法

通常情况下,在类中定义的所有函数(注意了,这里说的就是所有,跟self啥的没关系,self也只是一个再普通不过的参数而已)都是对象的绑定方法,对象在调用绑定方法时会自动将自己作为参数传递给方法的第一个参数。除此之外还有两种常见的方法:静态方法和类方法,二者是为类量身定制的,但是实例非要使用,也不会报错,后续将介绍。

1 静态方法

是一种普通函数,位于类定义的命名空间中,不会对任何实例类型进行操作,python为我们内置了函数staticmethod来把类中的函数定义成静态方法

class Foo:
    def spam(x,y,z): #类中的一个函数,千万不要懵逼,self和x啥的没有不同都是参数名
        print(x,y,z)
    spam=staticmethod(spam) #把spam函数做成静态方法

基于之前所学装饰器的知识,@staticmethod 等同于spam=staticmethod(spam),于是

class Foo:
    @staticmethod #装饰器
    def spam(x,y,z):
        print(x,y,z)

使用演示

print(type(Foo.spam)) #类型本质就是函数
Foo.spam(1,2,3) #调用函数应该有几个参数就传几个参数

f1=Foo()
f1.spam(3,3,3) #实例也可以使用,但通常静态方法都是给类用的,实例在使用时丧失了自动传值的机制

'''
<class 'function'>
1 2 3
3 3 3
'''

应用场景:编写类时需要采用很多不同的方式来创建实例,而我们只有一个__init__函数,此时静态方法就派上用场了

class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @staticmethod
    def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间
        t=time.localtime() #获取结构化的时间格式
        return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回
    @staticmethod
    def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
        t=time.localtime(time.time()+86400)
        return Date(t.tm_year,t.tm_mon,t.tm_mday)

a=Date('1987',11,27) #自己定义时间
b=Date.now() #采用当前时间
c=Date.tomorrow() #采用明天的时间

print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)

2 类方法

  类方法是给类用的,类在使用时会将类本身当做参数传给类方法的第一个参数,python为我们内置了函数classmethod来把类中的函数定义成类方法

class A:
    x=1
    @classmethod
    def test(cls):
        print(cls,cls.x)

class B(A):
    x=2
B.test()

'''
输出结果:
<class '__main__.B'> 2
'''

  应用场景:

import time
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @staticmethod
    def now():
        t=time.localtime()
        return Date(t.tm_year,t.tm_mon,t.tm_mday)

class EuroDate(Date):
    def __str__(self):
        return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)

e=EuroDate.now()
print(e) #我们的意图是想触发EuroDate.__str__,但是结果为
'''
输出结果:
<__main__.Date object at 0x1013f9d68>
'''

因为e就是用Date类产生的,所以根本不会触发EuroDate.__str__,解决方法就是用classmethod

import time
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    # @staticmethod
    # def now():
    #     t=time.localtime()
    #     return Date(t.tm_year,t.tm_mon,t.tm_mday)

    @classmethod #改成类方法
    def now(cls):
        t=time.localtime()
        return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化

class EuroDate(Date):
    def __str__(self):
        return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)

e=EuroDate.now()
print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿
'''
输出结果:
year:2017 month:3 day:3
'''

强调,注意注意注意:静态方法和类方法虽然是给类准备的,但是如果实例去用,也是可以用的,只不过实例去调用的时候容易让人混淆,不知道你要干啥

x=e.now() #通过实例e去调用类方法也一样可以使用,静态方法也一样
print(x)
'''
输出结果:
year:2017 month:3 day:3
'''

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏老司机的技术博客

人人都能学会的python编程教程9:dict和set

dict和我们小时候用的字典很像,如果你想查一个汉字,你只需要根据某一个拼音就可以很快速的查询到它。

6238
来自专栏程序员互动联盟

【今日问题】变量未初始化引起的崩溃

昨天写的今日问题,有小伙伴给我反馈,觉得挺有用,小编今天继续给小伙伴们总结遇到的常见问题 一、初学者经常由于没有养成良好的编程习惯,未初始化变量会引起那些问题 ...

2896
来自专栏屈定‘s Blog

Java--Enum的思考

枚举类是Java5引进的特性,其目的是替换int枚举模式或者String枚举模式,使得语义更加清晰,另外也解决了行为和枚举绑定的问题.

1374
来自专栏用户2442861的专栏

json格式

  1. “名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象(object),记录(reco...

2192
来自专栏简书专栏

Python入门

多媒体应用、WEB开发、网络爬虫、人工智能与机器学习、数据分析处理、服务器运维及其他小工具 知乎链接:用python做一些有趣的事情

2083
来自专栏老司机的技术博客

宝宝都能学会的python编程教程9:dict和set

dict字典 dict和我们小时候用的字典很像,如果你想查一个汉字,你只需要根据某一个拼音就可以很快速的查询到它。 ? 在字典的索引表里(比如部首表)查这个字...

3535
来自专栏北京马哥教育

对Python老司机99%有帮助的简明语法总结乱编

本文由马哥教育Python实战开发班6期学员推荐,转载自互联网,作者为赖笔小新,感谢作者的辛苦付出和贡献。 最近发现进入python群的朋友都在你是如何自学py...

3047
来自专栏求索之路

Effective Java笔记(不含反序列化、并发、注解和枚举)

最近把Effective Java复习了一遍,其中有比较多的java最佳实践可以在平时编程中用到。反序列化、并发、注解和枚举这四章没看,并发这本书里讲的比较简...

35711
来自专栏编程微刊

JS数组去重的6种算法实现以上就是为大家提供的6种JS数组去重的算法实现,希望对大家的学习有所帮助。

2042
来自专栏塔奇克马敲代码

第 16 章 模板与泛型编程

1596

扫码关注云+社区

领取腾讯云代金券