Python学习笔记之类与对象
这篇文章介绍有关 Python 类中一些常被大家忽略的知识点,帮助大家更全面的掌握 Python 中类的使用技巧
1、内置方法
(1)issubclass(class, classinfo)
检查 class 是否为 classinfo 的子类,classinfo 可以是一个类也可以是由多个类组成元组
注意 class 被认为是 class 的子类,也被认为是 object 的子类,若传入的类型与期望不符则抛出 TypeError 异常
(2)isinstance(object, classinfo)
检查 object 是否为 classinfo 的实例化对象,classinfo 可以是一个类也可以是由多个类组成元组
注意若 object 不是对象则返回 False,若 classinfo 既不是类也不是由多个类组成元组则抛出 TypeError 异常
(3)==
和 is
==
比较两对象是否 相等is
比较两对象是否 相同
>>> a = [1,2,3]
>>> b = [1,2,3]
>>> a == b # 比较两个对象的值是否相等
True
>>> a is b # 比较两个对象的 id 是否相等
False
(4)hasattr
,getattr
,setattr
和 delattr
hasattr(object, name)
:检查 object 中是否有特定属性 name,注意 name 需要用双引号包围getattr(object, name [, default])
:获取 object 中特定属性 name 的值若属性不存在,则抛出异常(没设置 default)或打印提示信息(有设置 default),注意 name 需要用双引号包围
setattr(object, name, value)
:设置 object 中特定属性 name 的值若属性不存在,则新建一个属性并赋值,注意 name 需要用双引号包围
delattr(object, name)
:删除 object 中特定属性 name 的值若属性不存在则抛出 AttributeError 异常,注意 name 需要用双引号包围
>>> class Person:
name = 'test'
def getName(self):
print(self.name)
>>> student = Person()
>>> hasattr(student,'name')
# True
>>> getattr(student,'age','none')
# 'none'
>>> setattr(student,'age',18)
>>> student.age
# 18
>>> delattr(student,'age')
>>> student.age
# AttributeError: 'Person' object has no attribute 'age'
2、属性隐藏
默认情况下,Python 允许在外部直接访问对象的属性
>>> class Test:
num = 0
def setNum(self,num): # 修改器
self.num = num
def getNum(self): # 访问器
return self.num
>>> obj = Test()
>>> # 我们可以直接访问对象的数据成员,而不需要通过访问器和修改器
>>> obj.num = 100
>>> obj.num
# 100
这似乎违反了隐藏的原则,因为在 Python 中没有为私有属性提供直接的支持
但是 Python 有另一种方式实现了类似于私有属性的效果,只需要让名称以两个下划线开头即可
>>> class Test:
__num = 0
def setNum(self,num):
self.num = num
def getNum(self):
return self.num
>>> obj = Test()
>>> # 此时,我们将不能直接访问对象私有的属性和方法
>>> obj.num
# AttributeError
>>> # 但是,我们依然可以使用访问器和修改器访问和修改数据
>>> obj.setNum(100)
>>> obj.getNum()
# 100
其实,这只是 Python 玩的一点小把戏,在类定义中,Python 对所有以两个下划线开头的名称进行了转换
即在开头加上下划线和类名,所以对于上面的例子我们依然可以直接访问对象的私有属性
>>> obj._Test__num = 100
>>> obj._Test__num
# 100
总之,我们无法禁止别人访问对象的私有方法和属性,只是以双下划线开头向对方发出了强烈的信号,希望他们不要这样做
如果你不希望名称被修改,又想让别人知道不应该从外部修改属性或方法,可以使用一个下划线开头
虽然这只是一种约定,但是还是有一些作用的,例如在使用 import 语句导入模块时,将不会导入以一个下划线开头的名称
3、参数 self
在类定义的方法中,传入的第一个参数一般为 self,self 究竟是什么呢?
学过 C++ 的朋友或许很容易理解,Python 中的 self 其实就相当于 C++ 中的 this,指向调用该方法的实例化对象
在刚刚接触时,常常会出现下面的错误
>>> # 常见错误:忘记在一般方法中加上 self 参数
>>> class Test:
def __init__(self):
self.value = 0
def getValue(): # 错误定义,没有加上参数 self
return value
>>> test = Test()
>>> test.getValue()
# TypeError: getValue() takes 0 positional arguments but 1 was given
>>> # 常见错误:忘记在方法中调用参数时加上 self,此时会默认该参数为局部变量
>>> class Test:
def __init__(self):
self.value = 0
def getValue(self): # 正确定义
return value # 错误使用,这里返回的是局部变量
>>> test = Test()
>>> test.getValue()
# NameError: name 'value' is not defined
在一般情况下,类方法都应该加上参数 self,但是也并非全部如此
在类方法中没有加上 self 参数的方法称为静态方法,它可以直接通过类来调用,而不可以通过对象调用
>>> class Test:
def show():
print('static function')
>>> test = Test()
>>> test.show() # 不可以通过对象调用
# TypeError: show() takes 0 positional arguments but 1 was given
>>> Test.show() # 但可以通过类直接调用
# static function
另外一个不需要加上 self 参数的情况是类方法
该方法传入的第一个参数是类似于 self 的参数 cls,该方法可通过对象直接调用,但参数 cls 将自动关联到类
>>> class Test:
def show(cls):
print('static function')
>>> test = Test()
>>> test.show() # 可以通过对象直接调用
# static function
4、魔法方法
魔法方法是一种特殊的方法,它的名称有以下的格式:__name__
,以两个下划线开头,以两个下划线结尾
(1)构造函数
__init__(self)
也被称为构造函数,该方法在创建对象时自动调用,返回值必须为 None,用于初始化对象
>>> class Test:
def __init__(self,value):
print('__init__ is called.')
self.value = value
def getValue(self):
return self.value
>>> obj = Test(16)
# __init__ is called.
>>> obj.getValue()
# 16
在继承机制中,方法重写 对构造函数尤为重要
对于一般的子类而言,在构造函数中不仅需要父类的初始化代码,还需要自己的初始化代码
要注意,在重写构造函数时,必须调用父类的构造函数,否则可能无法正确初始化对象
>>> class Person:
def __init__(self,name):
self.name = name
def show(self):
print('Name:', self.name)
>>> class Student(Person):
def __init__(self,stuID): # 构造函数重写,没有调用父类的初始化方法
self.stuID = stuID
def show(self): # 普通方法重写
print('Name:', self.name)
print('stuID:', self.stuID)
>>> student = Student(1234)
>>> student.show()
# AttributeError: 'Student' object has no attribute 'name'
我们可以看到上述的使用方法是错误的,因为在子类中没有调用父类的初始化方法,父类属性不能被正确初始化
为此,有两种解决方法:一是调用未关联的父类构造函数,二是使用函数 super
>>> # 调用未关联的父类构造函数,旧式用法
>>> class Student(Person):
def __init__(self,name,stuID): # 构造函数重写,调用父类的初始化方法
Person.__init__(self,name)
self.stuID = stuID
def show(self): # 普通方法重写
Person.show(self)
print('stuID:', self.stuID)
>>> student = Student('Peter',1234)
>>> student.show()
# Name: Peter
# stuID: 1234
>>> # 使用函数 super,新式用法
>>> class Student(Person):
def __init__(self,name,stuID): # 构造函数重写,调用父类的初始化方法
super().__init__(name) # 区别仅仅在于这一命令
self.stuID = stuID
def show(self): # 普通方法重写
super().show()
print('stuID:', self.stuID)
>>> student = Student('Peter',1234)
>>> student.show()
# Name: Peter
# stuID: 1234
在上面的例子中,可以看到两种处理方法的效果是一致的
但是调用未关联的父类构造函数难以处理多继承的问题,而使用函数 super 可以以同样的语法处理多继承问题
所以在新版的 Python 中,建议使用 super 来代替未关联的父类构造函数
事实上,__init__(self)
并不是实例化对象时第一个被调用的方法,第一个被调用的方法是 __new__(cls)
该方法的第一个参数是 cls,若还有其它参数则原封不动地传递给 __init__
__new__
方法一般返回一个实例化对象,正常情况下极少重写 __new__
方法
但当需要继承一个不可变方法,又需要对其进行修改时,可以重写该方法
>>> class MyString(str):
def __new__(cls,string):
string = string.upper()
return str.__new__(cls,string)
>>> obj = MyString('abcdefg')
>>> obj
# 'ABCDEFG'
(2)析构函数
__del__(self)
也被称为析构函数,该方法在对象销毁前自动调用
但事实上,在销毁对象时并不会立即调用 __del__(self)
方法,而是当所有指向该对象的标签被销毁时才会调用
>>> class Test:
def __del__(self):
print('__del__ is called')
>>> obj = Test()
>>> temp = obj
>>> del obj # 删除指向对象的标签时,并不直接调用 __del__
>>> del temp # 删除指向该对象的所有标签时,才会调用 __del__
# __del__ is called
(3)元素访问
使用本小节中介绍的魔法方法,可以帮助我们更好的创建序列和映射类型(元素集合),要实现它们的基本行为
不可变对象只需要实现 2 个方法(__len__
和 __getitem__
),
而可变对象则需要实现 4 个方法(__len__
和 __getitem__
加上 __setitem__
和 __delitem__
)
__len__(self)
:返回集合包含的项数,对序列而言是元素个数,对映射而言是键值对个数当对象使用内置函数
len
时被调用len(self)
__getitem__(self,key)
:返回与指定键相对应的值,对序列而言键是整数,对映射而言是键是任何类型当对象被访问时调用
self[key]
__setitem__(self,key,value)
:以与键相关联的方式储存值,当对象被修改时调用self[key] = value
__delitem__(self,key)
:删除与键相关联的值,当对象使用内置函数del
时被调用del self[key]
>>> class MyDic:
def __init__(self):
self.dic = dict()
def __getitem__(self,key):
print('__getitem__ is called')
return self.dic[key]
def __setitem__(self,key,value):
print('__setitem__ is called')
self.dic[key] = value
# 这里为了演示效果,没有实现 __del__ 方法,因此不能使用 del 删除其中的元素
>>> dic = MyDic()
>>> dic['A'] = 1 # 修改器
# __setitem__ is called
>>> dic['A'] # 访问器
# __getitem__ is called
# 1
>>> del dic['A']
# AttributeError: __delitem__
除了以上最基本的魔法方法可以帮助我们创建序列和映射类型外,还有其它一些有用的魔法方法
例如,__contains__(self,item)
定义使用成员运算符 in 或者 not in 时的行为
(4)属性访问
属性访问魔法方法可以拦截对对象属性的所有访问企图,一般可以用于执行权限检查,日志记录等操作
__getattribute__(self,name)
:在属性被访问时自动调用__getattr__(self,name)
:在属性被访问且对象没有该属性时自动调用__setattr__(self,name,value)
:在试图给属性赋值时自动调用__delattr__(self,name)
:在试图删除属性时自动调用
>>> class Test:
def __init__(self):
self.num = 0
def __setattr__(self,name,value):
print('__setattr__ is called')
self.__dict__[name] = value
# 注意,在这里使用 self.name = value 这样的常规赋值语句是错误的,
# 因为在语句中会再次调用 __setattr__,导致无限循环的错误
# 因此,可以使用对象内置属性 __dict__ 进行赋值
def __getattribute__(self,name):
print('__getattribute__ is called')
return super().__getattribute__(name)
# 同样,在这里使用 self.name 或 self.__dict__[name] 都是错误的
# 因为在语句中会再次调用 __getattribute__,导致无限循环的错误
# 因此,唯一安全的方法是使用 super
def __getattr__(self,name):
print('__getattr__ is called')
raise AttributeError
# 当无法找到属性时被调用,返回异常 AttributeError
>>> test = Test() # __init__ 被调用,执行属性 num 赋值
# __setattr__ is called
# __getattribute__ is called
>>> test.num # 执行属性 num 访问(num 属性存在)
# __getattribute__ is called
# 0
>>> test.void # 执行属性 void 访问(void 属性不存在)
# __getattribute__ is called
# __getattr__ is called
# AttributeError
(5)运算符
① 算术运算
二元运算符:
__add__(self, other)
:定义加法行为:+__sub__(self, other)
:定义减法行为:-__mul__(self, other)
:定义乘法行为:*__truediv__(self, other)
:定义真正除法行为:/__floordiv__(self, other)
:定义整数除法行为://__mod__(self, other)
:定义取模运算行为:%__pow__(self, other [, modulo])
:定义取幂行为:**__lshift__(self, other)
:定义按位左移行为:<<__rshift__(self, other)
:定义按位右移行为:>>__and__(self, other)
:定义按位与行为:&__or__(self, other)
:定义按位或行为:|__xor__(self, other)
:定义按位异或行为:^
注意:
- 反运算:在函数名前加一个字符 r,当左操作数不支持该算术运算时,则会利用右操作数调用该算术运算方法
- 增量赋值:在函数名前加一个字符 i,支持类似于 x *= y 的操作
一元操作符:
__pos__(self)
:定义正号行为:+__neg__(self)
:定义减号行为:-__abs__(self)
:定义绝对值行为:abs()__invert__(self)
:定义按位取反行为:~
② 比较运算
__lt__(self, other)
:定义小于行为:<__le__(self, other)
:定义小于等于行为:<=__eq__(self, other)
:定义等于行为:=__ne__(self, other)
:定义不等于行为:!=__gt__(self, other)
:定义大于行为:>__ge__(self, other)
:定义大于等于行为:>=
>>> class Test:
def __init__(self,value):
self.value = value
def __add__(self,other):
return Test(self.value + other.value)
def __neg__(self):
return Test(-self.value)
def __gt__(self,other):
return (self.value > other.value)
>>> test1 = Test(100)
>>> test2 = Test(1)
>>> test1 > test2
# True
>>> test3 = test1 + (-test2)
>>> test3.value
# 99
(6)输出函数
__str__(self)
:当使用函数 str 或使用函数 print 时被调用__repr__(self)
:当使用函数 repr 或直接输出对象时被调用
>>> class Test:
def __init__(self,value):
self.value = value
def __str__(self):
return str(self.value)
def __repr__(self):
return 'This is the class, Test'
>>> test = Test(100)
>>> str(test)
# '100'
>>> print(test)
# 100
>>> repr(test)
# 'This is the class, Test'
>>> test
# This is the class, Test
(7)描述符类
描述符类用于描述另一个类的属性,其将某种特殊类型的类实例指派给另一个类的属性
特殊类型的类需至少实现以下三个内置方法之一:
__get__(self, instance, owner)
:用于访问属性,返回属性的值self 为特殊类型的实例,instance 为另一个类的实例,owner 为另一个类的对象
__set__(self, instance, value)
:用于设置属性,不返回任何内容self为特殊类型的实例,instance为另一个类的实例,value为传入的值
__delete__(self, instance)
:用于删除属性,不返回任何内容self为特殊类型的实例,instance为另一个类的实例
在 Python 中有一个自带的描述符类 property,property(fget=None, fset=None, fdel=None,doc=None)
当指定 property 时,
- 若没有指定任何参数,则创建的特性既不可读也不可写
- 若指定一个参数( fget ),则创建的特性将是只读的
- 若指定两个参数( fget 和 fset ),则创建的特性将是可读可写的
- 第三个参数是可选的,指定用于删除属性的方法,这个方法不接受任何参数
- 第四个参数也是可选的,指定一个文档字符串
>>> class MyProperty: # 自己创建一个描述符类
def __init__(self,fget=None,fset=None,fdel=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
def __get__(self,instance,onwer):
print('__get__ is called')
return self.fget(instance)
def __set__(self,instance,value):
print('__set__ is called')
self.fset(instance,value)
def __delete__(self,instance):
print('__delete__ is called')
self.fdel(instance)
>>> class Test:
def __init__(self):
self._value = 0
def _getValue(self):
return self._value
def _setValue(self,value):
self._value = value
value = MyProperty(_getValue,_setValue)
>>> test = Test()
>>> test.value = 1
# __set__ is called
>>> test.value
# __get__ is called
# 1
【 阅读更多 Python 系列文章,请看 Python学习笔记 】
Python学习笔记之类与对象的更多相关文章
- python学习笔记4(对象/引用;多范式; 上下文管理器)
### Python的强大很大一部分原因在于,它提供有很多已经写好的,可以现成用的对象 21. 动态类型:对象/引用 对象和引用: 对象是储存在内存中的实体,对象名只是指向这一对象的引用(refere ...
- Python学习笔记总结(一)对象和流程语句总结
一.对象类型 1.数字 数字:不可变 2.字符串 字符串:不可原处修改[修改需要创建新的对象],有顺序,支持求长(len),合并(+),重复(*),索引S[0],分片(S[1:3]],成员测试(in) ...
- python学习笔记8--面向对象--属性和方法详解
属性: 公有属性 (属于类,每个类一份) 普通属性 (属于对象,每个对象一份) 私有属性 (属于对象,跟普通属性相似,只是不能通过对象直接访问) 方法:(按作用) 构造方法 析构函数 方法: ...
- python学习笔记8--面向对象编程
一.面向对象编程 面向对象--Object Oriented Programming,简称oop,是一种程序设计思想.在说面向对象之前,先说一下什么是编程范式,编程范式你按照什么方式来去编程,去实现一 ...
- python学习笔记——创建事件对象Event
1 Event对象的基本概述 用 multiprocessing.Event 实现线程间通信,使用multiprocessing.Event可以使一个线程等待其他线程的通知,我们把这个Event传递到 ...
- Python学习笔记——类和对象
类和对象 1. 一个例子 # 对象 = 属性 + 方法 # Python 中的类名称约定首字母大写,而 Python 函数名首字母小写 class Turtle: #属性 color = 'green ...
- webdriver(python)学习笔记六——操作测试对象
定位到具体对象后,就需要对其进行操作,比如点击.输入内容等. 一般来说,webdriver中比较常用的操作对象的方法有下面几个 click 点击对象 send_keys 在对象上模拟按键输入 clea ...
- Python学习笔记--2--面向对象编程
面向对象 类和装饰器@ #coding=gbk class student: def __init__(self,name,grand):#初始化构造函数,self相当于java中的this,相当于一 ...
- 《python基础教程(第二版)》学习笔记 类和对象(第7章)
<python基础教程(第二版)>学习笔记 类和对象(第7章) 定义类class Person: def setName(self,name): self.name=n ...
随机推荐
- 每日总结-05-19(AC自己主动机结束)
今天下午讨论了一下校赛的题,最终最终拍板,把校赛的题目定下来了. 然后今天A掉了4个AC自己主动机的题目.最终完毕了AC自己主动机专辑里面的15个题.至此AC自己主动机全然结束. 明天开启线段树专题. ...
- 10.0arcmap切片生成ptk步骤
注意:在制作之前需要点将图放到原本大小.并且保存一下不然容易造成数据丢失. 1.制作mxd 我们将待发布的数据,鼠标选中,拖入到ArcMap中间区域,单击保存. 可以对layers下面的图层进行改名. ...
- 关于逆元&&lucas定理
lucas是求组合数C(m,n)%p,有一个公式:C(m,n) = C(m/p,n/p)*C(m%p,n%p). (a*b)%c==a%c*b%c,但是(a/b)%c!=a%c/b%c,所以我们要算b ...
- APP-Android:APK
ylbtech-APP-Android:APK APK是AndroidPackage的缩写,即Android安装包(apk).APK是类似Symbian Sis或Sisx的文件格式.通过将APK文件直 ...
- md5的用处
MD5保存摘要及指纹信息 md5的用处: 1.保存用户密码2.校验数据的完整性
- Fishnet(几何)
http://poj.org/problem?id=1408 题意:给出 a1 a2 ... an b1 b2 ... bn c1 c2 . ...
- [转]"RDLC"报表-参数传递及主从报表
本文转自:http://www.cnblogs.com/yjmyzz/archive/2011/09/19/2180940.html 今天继续学习RDLC报表的“参数传递”及“主从报表” 一.先创建D ...
- POJ 2553 Tarjan
题意:如果v点能到的所有点反过来又能到v点,则v点是sink点,排序后输出所有的sink点. 思路:Tarjan缩点,输出所有出度为0的连通块内的点. PS:一定要记得把数组清零!!!!!!!否则自己 ...
- Asp.net三种事务处理
事务处理是在数据处理时经常遇到的问题,经常用到的方法有以下三种总结整理如下:方法1:直接写入到sql 中在存储过程中使用 BEGIN TRANS, COMMIT TRANS, ROLLBACK TRA ...
- Android环境的搭建遇到的问题和解决方案
安卓环境的搭建,我花了将近一天的时间,在最后终于找到了一个比较好的方案. 第一个问题是安卓的官网(http://developer.android.com)很难登录.SDK和ADT都是需要在官网上下载 ...