Python的魔法函数
概要
- 如何定义一个类
- 类里通常包含什么
- 各个部分解释
- 类是怎么来的
- type和object的关系
- 判断对象的类型
- 上下文管理器
类结构
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com # 类名后面写(object)这种写法表示是新式类,不写object是经典类,两种的区别是多继承的问题。
class OOP(object): # 定义类变量,类共享的,那么实例A修改以后实例B也会受到影响(修改不可比那对象感官上看不到修改,如果是可变类型比如列表,感官上就有影响了),
# 所以类变量是定义共有的属性通常不能修改,虽然技术上也可以修改。不用实例化也可以访问类变量。
var1 = "hello" # 不可变对象
list1 = [] # 可变对象 def __call__(self, *args, **kwargs):
"""
这个函数不是析构函数也不是构造函数,对象或者实例可以像函数一样调用,就像执行普通函数一样。这个如果不需要也不用写。
:param args:
:param kwargs:
:return:
"""
print("我是特殊函数 __call__") def __init__(self):
"""
构造函数,用于初始化类的实例,实例化对象的时候就会自动调用这个方法,比如把实例变量绑定到实例上。
这里这个self其实有些特别,也就是实例化的时候会自动把实例对象传递进来,self就是实例对象。
oop = OOP() 这里其实是 OOP(oop) 所以self就是实例oop,这个self也就是为了接受隐式传递进来的实例名称
除了在 __init__ 方法中定义的东西属于实例自己之外也就是在内存中也是独立的,其他的内容包括类变量、类里面的方法在内存中都只有一份所有
实例公用。
多说一句,其实实例化的时候先执行__new__方法,该方法调用__init__方法。
""" # 在 __init__ 方法里定义的变量是实例变量,实例独享的。通过实例调用变量或者其他资源是先找实例本身,如果没有就找类的。
# 比如类变量和实例变量同名,你通过实例访问这个变量时,它会给你返回实例变量的值。
# 加 "_" 表示私有变量,不过使用 "_" 也可以通过某种方式直接访问,所以要想使用严格的私有变量要使用 "__" 双下划线。
self._Var2 = ""
print("我是构造函数 __init__") def __del__(self):
"""
析构函数,当实例对象删除时候调用,所以不需要设置参数,你也传递不了参数。它是在实例销毁的时候自动调用的。
对于python解释器来说,它有垃圾回收机制,只要有实例存在解释器就认为这个类被使用。如果你del这个实例,其实你删除的
是这个实例,也就意味着切断了实例和类的关系,当解释器发现某个类没有被引用了就可以在内存中删除了。
实例保存的是指向类的指针,实例放在栈里,类放在堆里。基本数据类型放在栈里,非基本数据类型真实的数据都是在堆里,而这个变量名在栈里。
这个方法你通常不用写。
"""
print("我是析构函数 __del__") def myMethod(self):
"""
为什么类里面的每一个方法都是有self呢?因为我们调用的时候虽然是通过实例名称来调用,但是实际上是
OOP.myMethod(oop)这种形式,你发现它还是会自动把实例传进来,这个self就是用来接收这个实例的。所以
你在自己的方法里面可以访问变量。实例 oop 本身没有 myMethod()方法,之所以可以调用成功就是 OOP.myMonth(oop) 完成的。
对于实例来说类里面的方法都是公用的,你实例化多个对象其实这些对象是多个,但是它们所公用的类方法和类变量在内存中也只有一份,
哪个个实例调用这个方法那么这个self就是哪个实例。记住一句话实例调用方法都是对实例自身进程操作的。
再说详细点,2个针对于同一个类的实例,A实例操作一个方法会影响B实例么?显然不能,这就是self必须存在的原因,在JAVA中也是一样,只是它使用this。
:return:
"""
print("自定义方法") # 设置成属性用于获取内部变量
@property
def Var(self):
return self._Var2 # 设置成属性形式赋值给内部变量,这个必须写在@property下面
@Var.setter
def Var(self, value):
self._Var2 = value def main():
oop = OOP()
oop1 = OOP()
# 调用实例的 __call__函数
oop()
# 通过属性方式修改变量
oop.Var = "world"
print(oop.Var)
# 判断是否有某种属性
# print(hasattr(oop, "_var1")) # 修改不可变对象(类变量)
print("通过实例oop查看类变量var1的值:", oop.var1)
print("通过实例oop1查看类变量var1的值:", oop1.var1)
print("通过实例oop修改类变量var1的值:改为100")
oop.var1 = 100
print("通过实例oop查看类变量var1的值:", oop.var1)
print("通过实例oop1查看类变量var1的值:", oop1.var1)
# 从值上看感觉类变量也属于实例,因为修改不影响,下面打印id你就看出来了
# print("通过实例oop查看类变量var1的内存地址:", id(oop.var1))
# print("通过实例oop1查看类变量var1的内存地址:", id(oop1.var1))
# print("通过实例oop修改类变量var1的值:改为100")
# oop.var1 = 100
# print("通过实例oop查看类变量var1的内存地址:", id(oop.var1))
# print("通过实例oop1查看类变量var1的内存地址:", id(oop1.var1)) # 修改类变量可变类型变量
print("oop实例向list1添加一个元素A")
oop.list1.append("A")
print("oop的 list1 内容为:", oop.list1)
print("oop1的 list1 内容为:", oop1.list1) if __name__ == '__main__':
main()
关于类里面的类变量和实例变量还需要在说一下
#!/usr/bin/env python
# -*- coding: utf-8 -*- class A:
# 类变量
a = 11 def __init__(self, x, y):
# 实例变量
self.x = x
self.y = y a1 = A(1, 2)
# 这里你以为是修改的类A里面的a吗?其实不是,为什么,看下面
a1.a = 100
"""
这样定义其实会让a1这个实例多一个bb属性出来(新建到a1这个实例中),
所以上面那个a1.a 其实并不是A类里面的类变量a,而是属于实例自己的
"""
a1.bb = 200
print(a1.x, a1.y, a1.a, a1.bb) # 这也就是a1为什么会有bb a2 = A(5, 6)
# 这里的a值还是11,a2这个实例本身没有a这个变量,但是它自己没有就会向上查找,它的上一级就是A这个类
print(a2.x, a2.y, a2.a) A.a = 500 # 这样才会修改类变量,修改之后实例化的对象(之前或者之后)理论上都受到影响
print(A.a)
a3 = A(8, 9)
print("实例a3的a: ", a3.a) print("实例a2的a: ", a2.a)
# a1之所以不受影响是因为之前 a1自己定义了一个a属性,这样它自己就有a属性,从而就不用去找类的a属性了
print("实例a1的a: ", a1.a)
类中其他特殊方法(也叫做魔法方法)
__dic__
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com class TestObj(object): # 类变量
var1 = 999 def __init__(self, name, age):
self.name = name
self.age = age
self.list1 = ['A', 'B']
self.set1 = (1, 2)
self.dic1 = {"a": "a", "b": "b"} def main():
TO = TestObj(name="Tom", age=23)
print(TO.__dict__)
TO.name = "张三"
print(TO.__dict__) if __name__ == '__main__':
main()
作用:查看实例里面的属性,键为属性名,值为属性值,那都包含哪些属性呢? 所有 self.XXX 的都是属性。而且类变量也算。它永远返回的是实例当前的最新值。从输出看到类变量并没有输出。
其实这个属性通过类也可以调用
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com class TestObj(object): # 类变量
var1 = 999 def __init__(self, name, age):
self.name = name
self.age = age
self.list1 = ['A', 'B']
self.set1 = (1, 2)
self.dic1 = {"a": "a", "b": "b"} def main():
# TO = TestObj(name="Tom", age=23)
# print(TO.__dict__)
# TO.name = "张三"
# print(TO.__dict__) # 通过类来调用
print(TestObj.__dict__) if __name__ == '__main__':
main()
除了输出类变量之外还有一些类本身的东西。
__str__和__unicode__方法
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com class TestObj(object): # 类变量
var1 = 999 def __init__(self, name, age):
self.name = name
self.age = age def main():
TO = TestObj(name="Tom", age=23)
# 打印实例
print(TO) if __name__ == '__main__':
main()
输出是对象的地址
这是默认输出,如果你想改变输出就是通过__str__来设置的,__unicode__是python2的方法,在python3中使用__str__
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com class TestObj(object): # 类变量
var1 = 999 def __init__(self, name, age):
self.name = name
self.age = age # 定义__str__方法
def __str__(self):
return "我是TestObj" def main():
TO = TestObj(name="Tom", age=23)
# 打印实例
print(TO) if __name__ == '__main__':
main()
作用:在类里定义这个方法,那么在打印对象时将输出该方法的返回值。在python3中是__str__ 在python2中是__unicode__。这个方法用的最多的是在Django的module中。
__getitem__、__setitem__和__delitem__方法
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com class TestObj(object): # 类变量
var1 = 999 def __init__(self, name, age):
self.dic1 = {} def __getitem__(self, item):
print("__getitem__", item)
return self.dic1[item] def __setitem__(self, key, value):
print("__setitem__", key, value)
self.dic1[key] = value def __delitem__(self, key):
print("__deleteitem__", key) def main():
TO = TestObj(name="Tom", age=23)
TO["name"] = "Lucy"
TO["age"] = 23
print(TO.dic1)
print(TO["name"])
del TO["name"] if __name__ == '__main__':
main()
运行结果
通过这三个方法可以让操作实例跟操作字典一样。至于使用场景我也不知道,目前没有用到过。
这些魔法方法属于谁
#!/usr/bin/env python
# -*- coding: utf-8 -*- class Company(object):
def __init__(self, employee_list):
self.employee = employee_list def __getitem__(self, item):
return self.employee[item] company = Company(["Tom", "Lucy", "Lily"])
print(company[:2]) for item in company:
print(item)
换成字典就不行了
__getitem__可以让我们增加一些更加方便的方式去操作对象,但是上面之所以不能切片了是因为字典本身不能切片,而且__getitem__里面的item是索引并不是键,所以当我们换成字典的时候就会出错。其实这种魔法方法不是为类而创建的的也不是object才有的,而是一种可以丰富对类的操作的一种方式。
上下文管理器
#!/usr/bin/env python
# -*- coding: utf-8 -*- """
正常使用try语句我们是这样的
"""
def exe_try():
dic1 = {"A": "2"}
try:
print(dic1["B"])
except KeyError as e:
# 抛出异常执行这里
print("KeyError")
else:
# 不抛出异常执行这里
print("没有异常")
finally:
# 无论是否抛出异常最后都执行这里
print("finished") # exe_try() """
你是否想过一个问题,打开文件会抛出异常,通常打开文件后也需要关闭文件流,为什么用 with open()语句可以不用手动关闭文件流呢?
这就是上下文管理器
""" class Sample: def __init__(self):
# 首先执行这个方法
print("__init__") def __enter__(self):
# 然后会自动调用这个方法,可以理解为获取资源
print("__enter__")
return self def __exit__(self, exc_type, exc_val, exc_tb):
# 这个函数会自动调用,当跳出with语句的时候,目的是为了释放资源
print("__exit__") def toDo(self):
print("to do something") # 这个用法是不是很像 with open()呢?
with Sample() as sample:
sample.toDo() """
__enter__和__exit__构成了上下文管理器
"""
#!/usr/bin/env python
# -*- coding: utf-8 -*- """
如何把上下文管理器更加简化一下呢?
""" import contextlib @contextlib.contextmanager # 这个装饰器把下面的函数包装成上下文管理器,主要利用了yiele的特性
def myFun(arg1):
print("begin", arg1) # 相当于 __enter__ 里面的代码
yield {} # 这里必须有个生成器
print("finished") # 相当于 __exit__ 里面的代码 with myFun("AAA") as my:
print("BBB")
类是如何创建的
一切皆对象,任何一个对象都可以找到它属于什么类型,那么类也是对象,那类的类型是什么呢?
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com __metaclass__ = type class TestObj(object): def __init__(self, name, age):
self.name = name
self.age = age # 定义__str__方法
def __str__(self):
return "我是TestObj" def main():
TO = TestObj(name="Tom", age=23)
print(type(TO))
print(type(TestObj)) if __name__ == '__main__':
main()
输出
类的类型就是type,也就是说类是通过type来创建的。到底是怎么创建的呢?
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com def __init__(self, name, age):
self.name = name
self.age = age """
TestObj 对象名称
type() 函数
"TestObj" 类名称
(object,) 继承自哪里,可以为空
{} 方法名称和方法,字典形式 方法就是上面定义的
"""
TestObj = type("TestObj", (object,), {"__init__": __init__}) def main():
TO = TestObj(name="Tom", age=23)
print(type(TO))
print(type(TestObj)) if __name__ == '__main__':
main()
现在知道为什么TestObj的type是type了吧。
关于type和object
type用来生成类,而类用来生成实例,所以我们通过type命令可以查看这个实例或者说是对象是由谁生成的也就是其类型
a = 1
print(type(1), " 生成1")
print(type(int), " 生成int")
print(type(type), " 生成type")
# 基类
class MyClass:
pass # object是所有类都要继承的类也就是最顶层的类,或者说所有类的父类,类的祖先 print("MyClass的基类是:", MyClass.__bases__)
print("type的基类是:", type.__bases__)
print("object的type是:", type(object))
print("Object的基类是:", object.__bases__)
type的基类是object, 而object的type是type,看起来是个环形。object是type的实例,而type又继承自object,type也是自己的实例。在Python中
一切为对象,list、str、int等都是对象,可能有人问这命名是类啊,没错它们是类但也是对象。这一点和JAVA有所区别。这些东西之所有是类但同时也是对象
是因为他们都是type的实例。它们继承自object但是它们也是type的实例,只有实例才可以叫做对象,否则它们就是类。
type是自己的实例,object是type的实例,而type又继承了object,str是type的实例同时继承了objcet。
判断对象类型
我们知道获取对象类型通过type来查看。但是还有一个叫做isinstance,这两个有什么区别呢?
#!/usr/bin/env python
# -*- coding: utf-8 -*- class A:
pass class B(A):
def test(self):
print("Test") b = B() print(isinstance(b, B))
print(isinstance(b, A)) print(type(b) is A)
结果是type不认为实例b是A的类型,但是isinstance则任务b是A的类型,b是B的实例,而B继承了A所以说b是A的类型也没有错。那他俩有什么区别呢?
发现用 isinstance 和 type 得到的结果不同,因为type(b) 指向的就是B这个类,而A就是A这个类,显然不相同,虽然B继承自A,但是用 isinstance 就会得到相同的结果,它会追溯它判断b是不是A这个类型,因为b是B的实例,而B继承自A,所以结果为 True。
说到底type不会认为子类的对象的类型是父类,而isinstance则会子类的对象也是父类的类型。
多继承时super的执行顺序
#!/usr/bin/env python
# -*- coding: utf-8 -*- class A:
def __init__(self):
print("A") class B(A):
def __init__(self):
print("B")
# 调用父类方法,Python2写法
# super(Son, self).__init__() # 调用父类方法,Python3简化写法
super().__init__() # class C(A):
def __init__(self):
print("C")
super().__init__() # class D(B, C):
def __init__(self):
print("D")
super().__init__() # # 打印D的MRO看一下
print(D.__mro__) d = D()
常规上我们说super是调用父类方法,没错但是不太严谨,它是调用MRO路径寻找的下一个类的方法。当你使用单继承的时候看不出来,
当使用多继承的时候就会看出来。
__new__方法
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: rex.cheny
# E-mail: rex.cheny@outlook.com class TestObj(object): def __init__(self, name, age):
self.name = name
self.age = age
print("__init__方法执行了") def __new__(cls, *args, **kwargs):
"""
该方法会在__init__之前调用.
:return:
"""
print("__new__方法执行了") """
如果你的类里自己重写了 __new__ 方法那么下面的这个返回必须写,否则你定义的类将不会执行__init__方法。
这个类是继承自object,那么你其实并不知__new__原本具体都做什么,出于某些原因你必须要重写__new__方法,
当你的逻辑写完之后,就要通过 object.__new__(cls) 来调用父类的__new__方法,完成初始化。
"""
return object.__new__(cls) def main():
TO = TestObj(name="Tom", age=23)
print(TO.name) if __name__ == '__main__':
main()
Python的魔法函数的更多相关文章
- gj3 Python数据模型(魔法函数)
3.1 什么是魔法函数 类里面,实现某些特性的内置函数,类似 def __xx__(): 的形式. 不要自己定义XX,并不是和某个类挂钩的 class Company(object): def __i ...
- Python的魔法函数系列 __getattrbute__和__getattr__
#!/usr/bin/env python # -*- coding: utf-8 -*- import sys __metaclass__ = type """ _ ...
- python使用魔法函数创建可切片类型
#!/usr/bin/env python # -*- coding: utf-8 -*- """ 可切片的对象 """ import nu ...
- 16个python常用魔法函数
==,is的使用 ·is是比较两个引用是否指向了同一个对象(引用比较). ·==是比较两个对象是否相等 1.__ init__(): 所有类的超类object,有一个默认包含pass的__ init ...
- python常用魔法函数
1.__init__(): 所有类的超类object,有一个默认包含pass的__init__()实现,这个函数会在对象初始化的时候调用,我们可以选择实现,也可以选择不实现,一般建议是实现的,不实现对 ...
- Python的魔法函数之 - __len__,__getitem__,__setitem__,__delitem__
# 对象作为len()函数的参数是必须实现该方法 __len__ # 使用类似字典方式访问成员时必须实现 dic['pro_name'] __getitem__ # 使用类似字典方式设置成员时必须实现 ...
- Python魔法函数与两比特量子系统模拟
技术背景 本文主要涵盖两个领域的知识点:python的魔法函数和量子计算模拟,我们可以通过一个实际的案例来先审视一下这两个需求是如何被结合起来的. 量子计算模拟背景 ProjectQ是一个非常优雅的开 ...
- PythonI/O进阶学习笔记_2.魔法函数
前言: 本文一切观点和测试代码是在python3的基础上. Content: 1.什么是魔法函数,魔法函数__getitem__在python中应用. 2.python的数据模型和数据模型这种设计对p ...
- Python魔法函数
python中定义的以__开头和结尾的的函数.可以随意定制类的特性.魔法函数定义好之后一般不需要我们自己去调用,而是解释器会自动帮我们调用. __getitem__(self, item) 将类编程一 ...
随机推荐
- PCB Mark点相关
1)Mark点用于锡膏印刷和元件贴片时的光学定位.根据Mark点在PCB上的作用,可分为拼板Mark点.单板Mark点.局部Mark点(也称器件级MARK点) 2)拼板的工艺边上和不需拼板的单板上应至 ...
- 菜鸟安卓学习路——更强大的滚动控件--RecycleView
- The SQL Server instance returned an invalid or unsupported protocol version during login negotiatio
在使用.net core 连接sqlserver的时候遇到了这个问题 从字面意思理解大致是个什么版本不支持, 谷歌一下吧,ok,看到这个2000我就知道什么问题了 我的数据库还是2000的,总算把20 ...
- node04
1.模板引擎 用于渲染页面 介绍jade或ejs jade:侵入式,与原生html/css不共存,使用缩进代表层级 模板后缀.jade ejs:则非侵入式的 2.jade 1)简单使用: //代码 c ...
- Redis sentinel 哨兵模式
一.sentinel介绍 Sentinel作用: 1):Master状态检测 2):如果Master异常,则会进行Master-Slave切换,将其中一个Slave作为Master,将之前的Maste ...
- python 面试题知识回顾
1. python 函数 的参数传递 a = 1 def fun(a): a = 2 fun(a) print a # 1 a = [] def fun(a): a.append(1) fun(a) ...
- [LeetCode] Push Dominoes 推多米诺骨牌
There are N dominoes in a line, and we place each domino vertically upright. In the beginning, we si ...
- linux学习历程-不熟悉的linux命令
一:man(执行查看帮助命令) 二:常用的系统工作命令 1:echo echo命令用于显示在终端输出字符串或变量提取后的值,格式“echo [字符串]|[$变量]” 2:date 用于显示系统的时间和 ...
- Centos7 编译安装 Nginx PHP Mariadb Memcached 扩展 ZendOpcache扩展 (实测 笔记 Centos 7.3 + Openssl 1.1.0e + Mariadb 10.1.22 + Nginx 1.12.0 + PHP 7.1.4 + Laravel 5.4 )
环境: 系统硬件:vmware vsphere (CPU:2*4核,内存2G,双网卡) 系统版本:CentOS-7-x86_64-Minimal-1611.iso 安装步骤: 1.准备 1.0 查看硬 ...
- sql_update
学习: http://www.runoob.com/mysql/mysql-tutorial.html replace: update table_name set column = replace ...