进阶学Python:Python面向对象之属性方法
Posted 编程界的小胖子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进阶学Python:Python面向对象之属性方法相关的知识,希望对你有一定的参考价值。
这次我们主要介绍类中定义的各种属性,例如类属性、实例属性、类的私有属性,以及各种方法,例如实例方法、类方法、静态方法以及property 属性方法等相关知识。
🚀看一段代码
class Tool(object):
dog_type = "Huskie" # 类属性
def __init__(self,name):
self.name = name # 实例属性
# 实例方法
def tell_info(self):
pass
# 类方法
@classmethod
def bar(cls):
pass
# 静态方法
@staticmethod
def foo(x):
pass
#属性方法
@property
def tell_name(self):
return self.name
复制代码
类属性和实例属性
无论是类属性还是实例属性都是定义在类中,但是其根本区别是保存的位置和调用的对象不相同。例如:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File : dog.py
@Time : 2019/10/23 15:00:58
@Author : YongJia Chen
@Version : 1.0
@Contact : chen867647213@163.com
@License : (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc : None
'''
# here put the import lib
class Dogs(object):
# 类属性
belongTo = "Animals"
def __init__(self, name):
#实例属性
self.name = name
dog1 = Dogs("Sunny Chen")
print(Dogs.belongTo) # 类属性通过类访问
print(dog1.name) # 实例属性通过实例访问
print(dog1.belongTo) #类属性也可以被实例访问
# print(Dogs.name) # 但是实例属性无法被类访问
复制代码
类属性可以被类和属性访问,而实例属性只能被实例访问,这是由于:
这是由于每通过一个类创建一个实例对象,则会开辟一个内存空间,用来存放该实例对象的属性和方法,以及类对象的指针,实例对象之所以可以调用类中的方法,是因为可以通过类对象指针访问类的属性和方法。
但是若实例对象想修改类的属性和方法,则需要通过特殊的方法。例如:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File : dog.py
@Time : 2019/10/23 15:03:56
@Author : YongJia Chen
@Version : 1.0
@Contact : chen867647213@163.com
@License : (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc : None
'''
# here put the import lib
class Dogs(object):
# 类属性
belongTo = "Animals"
def __init__(self, name):
#实例属性
self.name = name
dog1 = Dogs("Sunny Chen")
dog1.belongTo = "wakka"
print(Dogs.belongTo) #Animals
# 注:通过这种方式是无法修改类属性的,而是在实例的属性字典中重写了该属性
print(dog1.__dict__) #{'name': 'Sunny Chen', 'belongTo': 'wakka'}
dog1.__class__.belongTo = "Wakka"
print(Dogs.belongTo) # Wakka--修改成功
复制代码
实例方法、类方法和静态方法
实例方法: 通常由对象调用,必须传入实例对象,执行实例方法时,自动将调用该方法的实例对象本身传给该方法的self参数。
类方法: 通常由类调用,必须传入类对象本身,执行类方法时,自动将调用该方法的类对象赋值给cls参数;
静态方法: 类和实例对象均可调用,不传实例对象和类对象,无默认参数。
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File : testDemo.py
@Time : 2019/10/23 15:09:21
@Author : YongJia Chen
@Version : 1.0
@Contact : chen867647213@163.com
@License : (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc : None
'''
# here put the import lib
class Test(object):
def __init__(self, name):
self.name = name
#实例方法
def mm(self):
print("in the mm")
#类方法
@classmethod
def tt(cls):
print("in the tt")
print(cls)
#静态方法
@staticmethod
def gg(a):
print("in the gg", a)
test1 = Test("alex")
test1.mm()
test1.tt() # 实例可以调用类方法,则默认将该类对象传入 --》<class '__main__.Test'>
test1.gg(100)
Test.mm(test1) # 类对象调用实例方法必须传入一个类
Test.tt()
Test.gg(100)
复制代码
相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
不同点:方法调用者不同、调用方法时自动传入的参数不同。
类方法和静态方法的作用:
类方法:主要使用类方法来管理类属性,无论是私有属性还是普通类属性,在很多类中,可能不需要实例化,仅仅是为了封装,这时候我们就可以通过类方法来管理类属性。
静态方法:当我们有许多杂乱且无联系的函数时,我们需要将函数封装在类中,而无法修改函数的代码即参数,仅仅是为了将这些函数通过类的方式封装起来,方便进行管理。例如:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File : toolDemo2.py
@Time : 2019/10/23 15:11:13
@Author : YongJia Chen
@Version : 1.0
@Contact : chen867647213@163.com
@License : (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc : None
'''
# here put the import lib
class Tool(object):
"""
定义一个工具箱,用来管理这些工具,仅仅是为了封装而不影响这些函数,方便管理
"""
@staticmethod
def hammer(): # 锤子
pass
@staticmethod
def ax(): # 斧头
pass
@staticmethod
def wrench(): # 扳手
pass
# 当我们需要使用这些工具是,直接用过类对象调用即可
# 同时不会传入额外的参数,例如self-实例本身,cls -类本身,仅仅是为了达到封装的效果
Tool.hammer()
Tool.ax()
复制代码
属性方法 property
字面意思就是 使方法变为一种特殊的属性,即一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法。例如:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File : testDemo.py
@Time : 2019/10/23 15:13:21
@Author : YongJia Chen
@Version : 1.0
@Contact : chen867647213@163.com
@License : (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc : None
'''
# here put the import lib
class Test(object):
def foo(self):
print("in the fo")
# 定义property属性
@property
def bar(self):
# print("in the bar")
return "in the bar"
tt = Test()
tt.foo() # 调用普通实例方法 -- in the fo
ret = tt.bar # 调用方式与类属性一样,并且也有返回值 -- in the bar
print(ret)
复制代码
定义时,在实例方法上加上@property装饰器,同时有且仅有一个self参数;
调用时,与调用类属性一样,无括号
# 对于京东商城中显示电脑主机的列表页面,每次请求不可能把数据库中的所有内容都显示到页面上,而是通过分页的功能局部显示,所以在向数据库中请求数据时就要显示的指定获取从第m条到第n条的所有数据 这个分页的功能包括:
# 根据用户请求的当前页和总数据条数计算出 m 和 n
# 根据m 和 n 去数据库中请求数据
class Pager:
def __init__(self, current_page):
# 用户当前请求的页码(第一页、第二页...)
self.current_page = current_page
# 每页默认显示10条数据
self.per_items = 10
@property
def start(self):
val = (self.current_page - 1) * self.per_items
return val
@property
def end(self):
val = self.current_page * self.per_items
return val
# 调用
p = Pager(1)
p.start # 就是起始值,即:m
p.end # 就是结束值,即:n
复制代码
总结:@property,将方法转化为一种特殊的属性的装饰器,故既然既然是属性,自然会有增删改查(调用、修改、删除)等,那么我们来看怎么实现property属性的增删改查。
property
属性的增删改查(两种方式):
两种方式 :
- 具有三种
@property
装饰器 ; - 类属性方式,创建值为
property对象
的类属性
三种@property装饰器
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File : goodDemo.py
@Time : 2019/10/23 15:17:57
@Author : YongJia Chen
@Version : 1.0
@Contact : chen867647213@163.com
@License : (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc : None
'''
# here put the import lib
# 方式一:通过装饰器,当执行某操作时,自动触发装饰器及装饰器下方法的执行(copy)
class Goods(object):
def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8
@property
def price(self):
# 实际价格 = 原价 * 折扣
new_price = self.original_price * self.discount
return new_price
@price.setter # func_name.setter
def price(self, value):
self.original_price = value
@price.deleter # func_name.deleter
def price(self, value):
del self.original_price
obj = Goods()
obj.price # 获取商品价格
obj.price = 200 # 修改商品原价
del obj.price # 删除商品原价
# 解析:有点类似于魔法,当执行某操作时,自动触发装饰器的运行
# @property -- 当获取price属性时,自动执行 @property 修饰的方法,并获取方法的返回值,例如:obj.func
# @func.setter --当设置修改price属性时,自动执行 @func.setter 修饰的 func 方法,并将值赋值给方法的参数,例如:obj.func = 200
# @func.deleter --当删除price属性时,自动执行 @func.deleter 修饰的func 方法
复制代码
类属性方式,创建值为 property对象
的类属性
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File : fooDemo.py
@Time : 2019/10/23 15:20:30
@Author : YongJia Chen
@Version : 1.0
@Contact : chen867647213@163.com
@License : (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc : None
'''
# here put the import lib
class Foo(object):
def get_bar(self):
num = 100
return num
def set_bar(self, value):
"""必须两个参数"""
num = value
return num
def del_bar(self):
num = 100
del num
return "deleted this attribute"
#顺序不能错,分别为 获取、修改、删除、描述
BAR = property(get_bar, set_bar, del_bar, "description...")
obj = Foo()
obj.BAR # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = 200 # 自动调用第二个参数中定义的方法:set_bar方法,并将200当作参数传入
del obj.BAR # 自动调用第三个参数中定义的方法:del_bar方法
desc = Foo.BAR.__doc__ # 自动获取第四个参数中设置的值:description...
print(desc)
print(obj.BAR)
复制代码
关于property属性的使用很常见,优势十分明显:
为用户提供一个简单的属性,而不再是复杂的函数(不同考虑函数的参数是什么、函数的返回值是什么),仅仅以调用属性的方式得到函数的返回值。本质上也是封装的体现即封装底层复杂的实现方式,给用户提供一个简单、使用的属性接口。以下是大公司的代码:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File : messageDemo.py 腾讯即时通信模块-参考
@Time : 2019/10/23 15:28:04
@Author : YongJia Chen
@Version : 1.0
@Contact : chen867647213@163.com
@License : (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc : None
'''
# here put the import lib
# coding: utf-8
import random
import time
class Message(object):
def __init__(self, msgarr=[], toacc=''):
self.msgbody = msgarr # 此处为MsgDict对象实例的列表或者空列表
self.toacc = toacc # toacc为字符串(单发)或者列表(批量发)
self.msgrandom = random.randint(1, 1000000000)
self.msgrequest = {
'To_Account': toacc, # 消息接收方账号
'MsgRandom': self.msgrandom, # 消息随机数,由随机函数产生
'MsgBody': [t.msg for t in msgarr]
}
def del_option(self, option):
if option in (set(self.msgrequest) -
set(['To_Account', 'MsgRandom', 'MsgBody'])):
self.__dict__.pop(option)
self.msgrequest.pop(option)
def append_msg(self, msg):
self.msgbody.append(msg)
self.msgrequest['MsgBody'].append(msg.msg)
def insert_msg(self, index, msg):
self.msgbody.insert(index, msg)
self.msgrequest['MsgBody'].insert(msg.msg)
def del_msg(self, index):
if index in range(len(self.msgbody)):
del self.msgbody[index]
del self.msgrequest['MsgBody'][index]
def set_from(self, fromacc):
# 指定消息的发送方,默认为服务器发送
self.fromacc = fromacc
self.msgrequest['From_Account'] = fromacc
def set_to(self, toacc):
# 指定消息的接收方,可以为String(单发),可以为List(批量发送)
self.toacc = toacc
self.msgrequest['To_Account'] = toacc
def refresh_random(self):
self.msgrandom = random.randint(1, 1000000000)
self.msgrequest['MsgRandom'] = self.msgrandom, # 消息随机数,由随机函数产生
def set_sync(self, sync):
# 同步选项:1, 把消息同步到From_Account在线终端和漫游上
# 2, 消息不同步至From_Account
# 若不填写,默认情况下会将消息同步
# 仅在单发单聊消息中可调用
self.sync = sync
self.msgrequest['SyncOtherMachine'] = sync
def set_timestamp(self):
# 设置消息时间戳,unix时间, 仅在单发单聊消息中可以调用
self.timestamp = int(time.time())
self.msgrequest['MsgTimeStamp'] = self.timestamp
def set_offlinepush(self, pushflag=0, desc='', ext='', sound=''):
# 仅适用于APNa推送,不适用于安卓厂商推送
self.msgrequest['OfflinePushInfo'] = {
'PushFlag': pushflag,
'Desc': desc,
'Ext': ext,
'Sound': sound
}
class MsgDict(object):
def __init__(self, msgtype='', msgcontent={}):
self.msgtype = msgtype
self.msgcontent = msgcontent
@property
def msg(self):
return {'MsgType': self.msgtype, 'MsgContent': self.msgcontent}
def set_content(self, content):
self.msgcontent = content
class TextMsg(MsgDict):
def __init__(self, text='', msgtype='TIMTextElem'):
self.text = text
content = {'Text': text}
super(TextMsg, self).__init__(msgtype=msgtype, msgcontent=content)
def set_text(self, text):
self.text = text
self.msgcontent['Text'] = text
class LocationMsg(MsgDict):
def __init__(self,
desc='',
latitude=0,
longitude=0,
msgtype='TIMLocationElem'):
self.desc = desc
self.latitude = latitude
self.longitude = longitude
content = {
'Desc': desc, # 地理位置描述信息, String
'Latitude': latitude, # 纬度, Number
'Longitude': longitude # 经度, Number
}
super(LocationMsg, self).__init__(msgtype=msgtype, msgcontent=content)
def set_desc(self, desc):
self.desc = desc
self.msgcontent['Desc'] = desc
def set_location(self, latitude, longitude):
self.latitude = latitude
self.longitude = longitude
self.msgcontent['Latitude'] = latitude
self.msgcontent['Longitude'] = longitude
def set_latitude(self, latitude):
self.latitude = latitude
self.msgcontent['Latitude'] = latitude
def set_longitude(self, longitude):
self.longitude = longitude
self.msgcontent['Longitude'] = longitude
class FaceMsg(MsgDict):
def __init__(self, index=1, data='', msgtype='TIMFaceElem'):
self.index = index
self.data = data
content = {
'Index': index, # 表情索引,用户自定义, Number
'Data': data # 额外数据, String
}
super(TextMsg, self).__init__(msgtype=msgtype, msgcontent=content)
def set_index(self, index):
self.index = index
self.msgcontent['Index'] = index
def set_data(self, data):
self.data = data
self.msgcontent['Data'] = data
class CustomMsg(MsgDict):
def __init__(self,
data='',
desc='',
ext='',
sound='',
msgtype='TIMCustomElem'):
self.data = data
self.desc = desc
self.ext = ext
self.sound = sound
content = {
'Data':
data, # 自定义消息数据。不作为APNS的payload中字段下发,故从payload中无法获取Data字段, String
'Desc': desc, # 自定义消息描述,当接收方为iphone后台在线时,做ios离线Push时文本展示
'Ext':
ext, # 扩展字段,当接收方为ios系统且应用处在后台时,此字段作为APNS请求包Payloads中的ext键值下发,Ext的协议格式由业务方确定,APNS只做透传
'Sound': sound # 自定义APNS推送铃声
}
super(CustomMsg, self).__init__(msgtype=msgtype, msgcontent=content)
def set_data(self, data):
self.data = data
self.msgcontent['Data'] = data
def set_desc(self, desc):
self.desc = desc
self.msgcontent['Desc'] = desc
def set_ext(self, ext):
self.ext = ext
self.msgcontent['Ext'] = ext
def set_sound(self, sound):
self.sound = sound
self.msgcontent['Sound'] = sound
class SoundMsg(MsgDict):
def __init__(self, uuid='', size=0, second=0, msgtype='TIMSoundElem'):
self.uuid = uuid
self.size = size
self.second = second
content = {
'UUID': uuid, # 语音序列号,后台用于索引语音的键值,String
'Size': size, # 语音数据大小, Number
'Second': second # 语音时长,单位秒 Number
}
super(SoundMsg, self).__init__(msgtype=msgtype, msgcontent=content)
def set_uuid(self, uuid):
self.uuid = uuid
self.msgcontent['UUID'] = uuid
def set_size(self, size):
self.size = size
self.msgcontent['Size'] = size
def set_second(self, second):
self.second = second
self.msgcontent['Second'] = second
class ImageMsg(MsgDict):
def __init__(self,
uuid='',
imgformat=0,
imginfo=[],
msgtype='TIMImageElem'):
self.uuid = uuid
self.imgformat = imgformat
self.imginfo = imginfo
content = {
'UUID': uuid, # 图片序列号,后台用于索引语音的键值,String
'ImageFormat':
imgformat, # 图片格式, BMP=1, JPG=2, GIF=3, 其他=0, Number
'ImageInfoArray':
[t.info for t in imginfo] # 原图,缩略图或者大图下载信息, Array
}
super(ImageMsg, self).__init__(msgtype=msgtype, msgcontent=content)
def set_uuid(self, uuid):
self.uuid = uuid
self.msgcontent['UUID'] = uuid
def set_format(self, imgformat):
self.imgformat = imgformat
self.msgcontent['ImageFormat'] = imgformat
def append_info(self, info):
# info 为ImageInfo对象实例
self.imginfo.append(info)
self.msgcontnet['ImageInfoArray'].append(info.info)
def insert_info(self, index, info):
self.imginfo.insert(index, info)
self.msgcontent['ImageInfoArray'].insert(index, info.info)
def del_info(self, index):
del self.imginfo[index]
del self.msgcontent['ImageInfoArray'][index]
class FileMsg(MsgDict):
def __init__(self, uuid='', size=0, name='', msgtype='TIMFileElem'):
self.uuid = uuid
self.size = size
self.name = name
content = {
'UUID': uuid, # 文件序列号,后台用于索引语音的键值,String
'FileSize': size, # 文件数据大小, Number
'FileName': name # 文件名称/路径, String
}
super(FileMsg, self).__init__(msgtype=msgtype, msgcontent=content)
def set_uuid(self, uuid):
self.uuid = uuid
self.msgcontent['UUID'] = UUID
def set_size(self, size):
self.size = size
self.msgcontent['FileSize'] = size
def set_name(self, name):
self.name = name
self.msgcontent['FileName'] = name
class ImageInfo(object):
def __init__(self, itype=1, size=0, width=0, height=0, url=''):
#图片类型, 1-原图, 2-大图, 3-缩略图, 0-其他
self.itype = itype
# 图片数据大小,Number
self.size = size
# 图片宽度,Number
self.width = width
# 图片高度, Number
self.height = height
# 图片下载地址,String
self.url = url
@property
def info(self):
return {
'Type': self.itype,
'Size': self.size,
'Width': self.width,
'Height': self.height,
'URL': self.url
}
def set_type(self, itype):
self.itype = itype
def set_size(self, size):
self.size = size
def set_width(self, width):
self.width = width
def set_height(self, height):
self.height = height
def set_url(self, url):
self.url = url
复制代码
快去动手试试吧!
① 2000多本Python电子书(主流和经典的书籍应该都有了)
② Python标准库资料(最全中文版)
③ 项目源码(四五十个有趣且经典的练手项目及源码)
④ Python基础入门、爬虫、web开发、大数据分析方面的视频(适合小白学习)
⑤ Python学习路线图(告别不入流的学习)
需要相关资料的可以通过扫一扫
以上是关于进阶学Python:Python面向对象之属性方法的主要内容,如果未能解决你的问题,请参考以下文章