1. 类和对象

类(class)是用来描述具有相同属性(attribute)和方法(method)的对象的集合,对象(object)是类(class)的具体实例。比如学生都有名字和分数,他们有着共同的属性。这时我们就可以设计一个学生类, 用于记录学生的名字和分数,并自定义方法打印出他们的名字和方法。

class Student(object):def __init__(self,name,age,score):self._name = nameself._age = ageself._score = scoredef show(self):print("{}的年龄是{}岁,分数是{}分".format(self._name,self._age,self._score))aStudent = Student("瑞雯",18,99)
  • 属性:类里面用于描述所有对象共同特征的变量或数据。如_name_age_score
  • 方法:类里面的函数,用来区别类外面的函数, 用来实现某些功能。如show()
  • 对象(实例):根据类创建出来的,如aStudent

2. 属性

2.1 类属性

定义在类中函数体外的属性,属于类共有属性

class A(object):count = 0def __init__(self):# 类中只有通过类名.类属性访问,self.__class__返回的就是类名self.__class__.count += 1   # 或 A.count += 1a = A()
b = A()# 类外既可以通过类来访问,也可通过类的实例来访问
print(a.count)  # 2
print(A.count)  # 2

要修改类属性必须通过类名.属性的方式来修改,而不能通过实例,因为实例.属性这种方式其实是创建了同名的实例属性,屏蔽了类的属性,通过del 实例.属性操作后,会发现实例.属性还是之前的。

2.2 实例属性

定义在__init__()方法中,属于对象

class B(object):def __init__(self,name,age):self.name = nameself.age = agea = B("易",18)
b = B("金克斯",8)

一般来说,对象的属性应该设为private,不能被外界直接访问,这里暂且不管访问权限问题。当能从外界直接访问时,必须通过对象.属性来访问,这时候属性是属于对象实例的。

3. 访问控制

Python中并没有publicprotectedprivate这样的关键字,所以无法实现数据封装,只能从语法上来定义可见性,依靠程序员自觉遵守规约。

Python中,不存在函数的重载,因为函数名和普通变量名一样,都是引用,指向一个对象,是一对一的关系,都是标识符。Python中就是通过标识符的命名来区分访问的可见性。

  • 字符开头的标识符,如:age
    这种标识符相当于public,可以通过对象.属性或者对象.方法来执行。

  • 双下划线开头结尾的标识符,如: __init__
    用户最好不要自定义这种类型的标识符,因为这通常是系统调用。

  • 单下划线开头的标识符,如:_age
    这种标识符相当于protected,不过Python中没有protected的概念,所以被视为private,但是,你可以按照public用,属于推荐private但是可以public的。

  • 双下划线开头的标识符,如:name
    相当于privatePython通过更改标识符名(改为_类名__标识符)来实现无法访问的机制。

4. 方法

4.1 实例方法

class Test(object):def __init__(self,name,age):self.__name = nameself.__age = agedef grow(self,growth):self.__age += growth

只能被对象实例调用,第一个参数必须是self,它指向调用这个方法的实例。

4.2 类方法

class Test(object):def __init__(self,name,age):self.__name = nameself.__age = age@classmethoddef getInstance(cls):return cls("薇恩",16)

绑定到类的方法,必须在定义函数的上方使用 @classmethod 装饰器,同时,第一个参数必须是 cls,这个 cls 代表这个类的类型,如上面例子中的Test,所以在类方法内部可以用 cls(参数) 创建对象,也可以用它调用属于类的属性、其他可使用的方法。对外,类方法可以通过类调用,也可以通过实例对象来调用类方法。

4.3 静态方法

class Test(object):def __init__(self,name,age):self.__name = nameself.__age = age@staticmethoddef static_func():print("正在调用静态方法")

静态方法不像前两个有特定参数,比较自由,即使你强行把第一个参数名写出selfcls,也不会有相应的作用。静态方法可以通过类或者实例对象调用。

4.4 property方法

内置函数property()是用来使类的属性能像Java Bean那样操作的函数,它的函数定义为:

property(fget=None, fset=None, fdel=None, doc=None)
  • fget:    function to be used for getting an attribute value
  • fset:    function to be used for setting an attribute value
  • fdel:    function to be used for deleting an attribute
  • doc:    docstring
class C(object):def getx(self): return self._xdef setx(self, value): self._x = value + 1def delx(self): del self._xx = property(getx, setx, delx)c = C()
c.x = 5
print(c.x)  # 6
del c.x
# print(c.x) AttributeError: 'C' object has no attribute '_x'

这样,既能使对象能简单调用属性,也能保证数据的封装,可是,这么写太麻烦,普遍用的形式是使用装饰器来实现Bean:

class Hero(object):def __init__(self,name):self.__name = name@propertydef name(self):print("getter方法...")return self.__name@name.setterdef name(self,value):print("setter方法...")self.__name = value@name.deleterdef name(self):print("deleter方法...")del self.__namep = Hero("蝙蝠侠")
p.name                      # getter方法...
p.name = "游城十代"          # setter方法...
del p.name                  # deleter方法...

一般@property只用于私有属性“公有化”,并且getter方法和deleter方法只能有self参数。

5. 类的特殊成员

5.1 __doc__

表示类的描述信息

class Foo(object):'''Foo的描述'''print(Foo.__doc__)                 # Foo的描述

5.2 __module____class__

  • __module__表示当前操作的对象在那个模块
  • __class__表示当前操作的对象的类是什么,也就是谁创建了这个类,metaclass还是type
class Foo(object):passprint(Foo.__module__)                    # __main__
print(Foo.__class__)                    # <class 'type'>

5.3 __all__

__all__是一个字符串list,用来定义模块中对于from XXX import *时要对外导出的符号,即要暴露的借口,但它只对import *起作用,对from XXX import XXX不起作用

__all__ = ['MNIST', 'FashionMNIST', 'CIFAR10', 'CIFAR100','ImageRecordDataset', 'ImageFolderDataset']

5.4 __init__

构造方法,创建对象时自动执行的初始化函数

class Foo(object):def __init__(self):print("init方法...")f = Foo()  # init方法...

5.5 __del__

  • 析构方法,当对象在内存中被释放时,自动触发执行
  • 此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
class Foo:def__del__(self):pass

5.6 __call__

  • 对象后面加括号,触发执行
  • 构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Demo(object):def __init__(self):print("执行init...")def __call__(self, *args, **kwargs):print("执行call...")d = Demo()  # 执行init...
d()  # 执行call...

5.7 __dict__

  • 返回类或对象中的所有成员
  • 普通字段属于对象,静态字段和方法属于类
class Province(object):country = 'China'def __init__(self, name, count):self.name = nameself.count = countdef func(self, *args, **kwargs):print('func')print(Province.__dict__)                    # 返回类的所有成员
'''
{'__module__': '__main__', 'country': 'China',
'__init__': <function Province.__init__ at 0x0000022A0C5278C8>,
'func': <function Province.func at 0x0000022A0C527950>,
'__dict__': <attribute '__dict__' of 'Province' objects>,
'__weakref__': <attribute '__weakref__' of 'Province' objects>,'__doc__': None}
'''print(Province("卡兹克",1).__dict__)       # 返回对象的成员
'''
{'name': '卡兹克', 'count': 1}
'''

5.8 __str____repr__

  • 都是更改类的显示方式
  • __str__是给用户看到的字符串,__repr__是给开发者看的,比如debug时,变量列表显示的是__repr__函数返回的内容
  • 一般情况下只用写一个__str__,然后__repr__ = __str__
class Text(object):def __init__(self,text):self.__text = textdef __str__(self):return self.__text__repr__ = __str__  # 一般都这么写偷懒t = Text("无极剑圣")
print(t)  # 无极剑圣

5.9 __getitem____setitem____delitem__

  • 用于索引操作,如字典。以上分别表示获取、设置、删除数据
  • __getitem__也可以传入切片
class Subject(object):def __getitem__(self, item):  # 根据item返回一个值print("调用getitem...")def __setitem__(self, key, value):  # 设置key对应的值为valueprint("调用setitem...")def __delitem__(self, key):     # 删除这组键值对print("调用delitem...")m = Subject()
n = m['math']  # 调用getitem...
m['math'] = 100  # 调用setitem...
del m['math']  # 调用delitem...

5.10 __iter____next__

用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了__iter____next__

class Fib(object):def __init__(self):self.a, self.b = 0, 1  # 初始化两个计数器a,bdef __iter__(self):return self  # 实例本身就是迭代对象,故返回自己def next(self):self.a, self.b = self.b, self.a + self.b  # 计算下一个值if self.a > 100000:  # 退出循环的条件raise StopIteration();return self.a  # 返回下一个值

5.11 __slots__

一般情况下,可以任意地给对象添加属性,这会造成很大的漏洞,影响程序安全,为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性

class Student(object):__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称s = Student()
s.name = 'Michael'
s.age = 25
s.score = 99  # AttributeError: 'Student' object has no attribute 'score'

显然,这时候不能添加限制以外的属性,但要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类的属性不做限制,除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身__slots__加上父类__slots__

其他

例如各种重载运算符的函数,需要用到Google即可

6. 创建对象

6.1 传统创建类

class Demo(object):def __init__(self,name):self.__name = named = Demo("乐芙兰")

d是通过Demo类实例化的对象,其实不仅d是一个对象,Demo本身也是一个对象,因为在Python中,一切皆对象,d是通过执行Demo类的构造方法创建,那么Demo也应该是通过某个类的构造方法创建。

print(type(d))  # <class '__main__.Demo'>    表示d由Demo类创建
print(type(Demo))  # <class 'type'>          表示Demo由type类创建

所以,d对象是Demo类的一个实例,Demo类对象是 type 类的一个实例,即:Demo类对象是通过type类的构造方法创建。

6.2 type创建类

  • 语法:type('类名',父类的元组,成员字典)
def init(self,name):self.__name = namedef show(self):print(self.__name)Demo = type('Demo',(object,),{'__init__':init,'output':show,'a':3})
d = Demo("诡术妖姬")
print(d)  # <__main__.Demo object at 0x0000017F3C0F32E8>
d.output()  # 诡术妖姬
  • 一般用类名同名的变量来接受创建的类
  • 父类只有object时,注意元组单元素时的逗号,成员字典中,成员名是字符串,对应的值可以是方法地址,可以是属性值
  • initshow这些方法可以在前面加 @classmethod 或者 @staticmethod 等,来定义类函数和静态函数

6.3 __new__方法

__new__方法是类自带的一个方法,可以重写,__new__方法在实例化的时候也会执行,并且先于__init__方法之前执行,简单理解,创建对象和初始化对象。

class Foo(object):def __init__(self, name):self.name = nameprint("Foo __init__")def __new__(cls, *args, **kwargs):print("Foo __new__", cls, *args, **kwargs)return object.__new__(cls)f = Foo("凯特琳")
"""
Foo __new__ <class '__main__.Foo'> 凯特琳
Foo __init__
"""

6.4 重写__new__方法

  • 重写时,必须要调用父类的_new__方法,不然会覆盖父类的__new__方法,实例创建不了
  • __new__()方法创建出该类的实例,然后返回该实例给__init__()方法调用。__init__()方法的self就是__init__()方法创建返回的
  • 依照Python官方文档的说法,__init__()方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径,还有就是实现自定义的metaclas

继承不可变对象的例子

假如我们需要一个永远都是正数的整数类型,通过继承int

class PositiveInteger(int):def __new__(cls, value):return super(PositiveInteger, cls).__new__(cls, abs(value))i = PositiveInteger(-3)print(i)  # 3

这时候可能有的人会通过__init__方法来使-3变为3,但是,因为继承于int,是不可变的,__new__创建完时,值已经确定了,再通过__init__修改是不行的。

__new__方法与__init__方法的关系

class A(object):def __init__(self):print(self)print("这是__init__()方法...")def __new__(cls):print(id(cls))print("这是__new__()方法...")ret = super().__new__(cls)print(ret)return retprint(id(A))
a = A()
'''
输出:21512796548242151279654824这是__new__()方法...<__main__.A object at 0x000001F4E4406FD0><__main__.A object at 0x000001F4E4406FD0>这是__init__()方法...
'''

单例模式

class Number(object):__instance = Nonedef __new__(cls, val):if cls.__instance is None:cls.__instance = super().__new__(cls)cls.__instance.__value = valreturn cls.__instanceelse:return cls.__instancedef __str__(self):return str(self.__value)m = Number(-3)
n = Number(-6)print(m)  # -3
print(n)  # -3print(id(m))  # 2199891453768
print(id(n))  # 2199891453768

metaclass

  • metaclass,直译为元类,简单的解释就是,当我们定义了类以后,就可以根据这个类创建出实例,所以,先定义类,然后创建实例。但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以,先定义metaclass,然后创建类,连接起来就是:先定义metaclass,就可以创建类,最后创建实例。所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。

  • metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用metaclass的情况,所以,以下内容看不懂也没关系,因为基本上你不会用到

我们先看一个简单的例子,这个metaclass可以给我们自定义的MyList增加一个add方法。定义ListMetaclass,按照默认习惯,metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass

# metaclass是创建类,所以必须从type类型派生:
class ListMetaclass(type):def __new__(cls, name, bases, attrs):attrs['add'] = lambda self, value: self.append(value)return type.__new__(cls, name, bases, attrs)class MyList(list,metaclass=ListMetaclass):pass

当我们写下metaclass = ListMetaclass语句时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义

测试一下MyList是否可以调用add()方法:

l = MyList()
l.add(1)
print(l)  # [1]

元类中,最好只定义__new__()方法,因为其他类如果使用了“metaclass = XXXMetaclass”,只会调用这个metaclass__new__()方法来创建类,也就是避免了直接创建类,在创建类之前还封装了一组操作,但是,metaclas中其他的方法、属性等,是不会让使用metaclass创建的类继承的,所以一般在元类中,只写__new__()方法。metaclass可以隐式继承到子类

『Python』面向对象(一)相关推荐

  1. 『Python』__getattr__()特殊方法

    self的认识 & __getattr__()特殊方法 将字典调用方式改为通过属性查询的一个小class, class Dict(dict):def __init__(self, **kw): ...

  2. python多进程优化_『Python』多进程处理

    尝试学习python的多进程模组,对比多线程,大概的区别在: 1.多进程的处理速度更快 2.多进程的各个子进程之间交换数据很不方便 多进程调用方式 进程基本使用multicore() 进程池优化进程的 ...

  3. 『Python』VS2015编译源码注意事项

    一.2.5.6版本源码编译 解压 Python-2.5.6.tgz 进入 Pcbuild8 文件夹,使用 vs 2013 打开 pybuild.sln (vs 解决方案),进入 vs2015IDE 环 ...

  4. python控制语句中的条件语句_『Python』条件控制语句

    Loading... ## 1. 条件语句 ``` Python条件语句是通过一条或者多条语句的执行结果(True或False)来决定执行的代码块. ``` ``` 在Python中, 指定任何非0和 ...

  5. 『Python』python 弹窗、提示和警告框MessageBox部件

    需要安装pywin32模块 pip install pywin32 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com i ...

  6. python查看对象占用内存_『Python』内存分析_List对象内存占用分析

    list声明后结构大体分为3部分,变量名称--list对象(结构性数据+指针数组)--list内容,其中id表示的是list对象的位置, v引用变量名称,v[:]引用list对象,此规则对python ...

  7. 『Python』Excel文件的读取以及DataFrame的相关操作 (2)

    最近经常使用 p y t h o n python python 对 E x c e l Excel Excel 文件进行 E T L ETL ETL 操作,其中 p a n d a s pandas ...

  8. python中setup是什么意思_『Python』setup.py简介

    setup.py应用场合 网上见到其他人这样介绍: 假如我在本机开发一个程序,需要用到python的redis.mysql模块以及自己编写的redis_run.py模块.我怎么实现在服务器上去发布该系 ...

  9. 『Python』Python编译成计算机可执行文件(Windows计算机可执行文件exe,Linux 计算机可执行文件elf,Mac计算机可执行文件mach-O)

    文章目录 背景: 我们的主角 :PyInstaller 使用 1.安装pyinstaller 2.进入安装工作环境 3. 生成不自定义图标的exe文件 4.生成带自定义图标的exe文件 PyInsta ...

最新文章

  1. Java8的集合:HashMap的实现原理
  2. R语言使用psych包的fa函数对指定数据集进行因子分析(输入数据为相关性矩阵)、使用rotate参数指定进行斜交旋转提取因子、使用factor.plot函数可视化斜交旋转因子分析、并解读可视化图形
  3. I.MX6 I2C DS1337 disable square-wave output
  4. ninject 的 实现 的 理解
  5. Android Studio报Element XXXX must be declared的解决方法
  6. Mybatis Plus——AutoGenerator配置BaseResultMap(通用查询映射结果)和BaseColumnList(通用查询结果列)自动生成
  7. SAP CRM settype COMM_PROD_VAR
  8. 真的,太多人辜负了.NET5!
  9. 浅析Kubernetes Pod重启策略和健康检查
  10. JS函数生成器,function* () {}
  11. Unity3d场景漫游---iTween实现
  12. 常见数学建模方法学习总结及资料汇总
  13. 12864 c语言,单片机12864 c程序
  14. 汉字编码及区位码查询算法
  15. 宏基因组 微生物组 微生态杂志简介及2019最新影响因子
  16. 黑马程序员_银行业务调度系统
  17. Hive实战—时间滑动窗口计算
  18. 集合的简单实现(斗地主牌的实现 )
  19. 【c++】string类的模拟实现(下)
  20. java常用二进制数据转换工具

热门文章

  1. 用python画星空的代码
  2. 半夜,滴滴司机问我会 LRU 吗?
  3. iphone手机上使用lrz压缩之后图片旋转的问题
  4. 微信小程序的 websocket 以及 微信开发者工具测试 ws 协议没有数据的 离奇解决方案 记录
  5. 理发店里看到的成功格言
  6. confuse(iOS马甲包混淆,上架神器)
  7. RequestBodyAdvice 和 ResponseBodyAdvice增强器使用
  8. git创建远程分支并将本地分支与远程分支关联
  9. 项目冲刺之任务场景分析(一)
  10. CodeForces 1152D Neko and Aki's Prank