『Python』面向对象(一)
类和对象
类(class)是用来描述具有相同属性(attribute)和方法(method)的对象的集合,对象(object)是类(class)的具体实例。比如学生都有名字和分数,他们有着共同的属性。这时我们就可以设计一个学生类, 用于记录学生的名字和分数,并自定义方法打印出他们的名字和方法。
class Student(object):
    def __init__(self,name,age,score):
        self._name = name
        self._age = age
        self._score = score
    def show(self):
        print("{}的年龄是{}岁,分数是{}分".format(self._name,self._age,self._score))
aStudent = Student("瑞雯",18,99)
- 属性:类里面用于描述所有对象共同特征的变量或数据。如_name、_age、_score
- 方法:类里面的函数,用来区别类外面的函数, 用来实现某些功能。如show()
- 对象(实例):根据类创建出来的,如aStudent
属性
类属性
定义在类中函数体外的属性,属于类共有属性
class A(object):
    count = 0
    def __init__(self):
         # 类中只有通过类名.类属性访问,self.__class__返回的就是类名
        self.__class__.count += 1	# 或 A.count += 1
a = A()
b = A()
# 类外既可以通过类来访问,也可通过类的实例来访问
print(a.count)  # 2
print(A.count)  # 2
要修改类属性必须通过
类名.属性的方式来修改,而不能通过实例,因为实例.属性这种方式其实是创建了同名的实例属性,屏蔽了类的属性,通过del 实例.属性操作后,会发现实例.属性还是之前的。
实例属性
定义在__init__()方法中,属于对象
class B(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age
a = B("易",18)
b = B("金克斯",8)
一般来说,对象的属性应该设为private,不能被外界直接访问,这里暂且不管访问权限问题。当能从外界直接访问时,必须通过
对象.属性来访问,这时候属性是属于对象实例的。
访问控制
在Python中并没有public,protected,private这样的关键字,所以无法实现数据封装,只能从语法上来定义可见性,依靠程序员自觉遵守规约。
在Python中,不存在函数的重载,因为函数名和普通变量名一样,都是引用,指向一个对象,是一对一的关系,都是标识符。Python中就是通过标识符的命名来区分访问的可见性。
- 字符开头的标识符,如: - age
 这种标识符相当于public,可以通过- 对象.属性或者- 对象.方法来执行。
- 双下划线开头结尾的标识符,如: - __init__
 用户最好不要自定义这种类型的标识符,因为这通常是系统调用。
- 单下划线开头的标识符,如: - _age
 这种标识符相当于protected,不过Python中没有protected的概念,所以被视为private,但是,你可以按照public用,属于推荐private但是可以public的。
- 双下划线开头的标识符,如: - name
 相当于private,Python通过更改标识符名(改为- _类名__标识符)来实现无法访问的机制。
方法
实例方法
class Test(object):
    def __init__(self,name,age):
        self.__name = name
        self.__age = age
    def grow(self,growth):
        self.__age += growth
只能被对象实例调用,第一个参数必须是self,它指向调用这个方法的实例。
类方法
class Test(object):
    def __init__(self,name,age):
        self.__name = name
        self.__age = age
    @classmethod
    def getInstance(cls):
        return cls("薇恩",16)
绑定到类的方法,必须在定义函数的上方使用 @classmethod 装饰器,同时,第一个参数必须是 cls,这个 cls 代表这个类的类型,如上面例子中的Test,所以在类方法内部可以用 cls(参数) 创建对象,也可以用它调用属于类的属性、其他可使用的方法。对外,类方法可以通过类调用,也可以通过实例对象来调用类方法。
静态方法
class Test(object):
    def __init__(self,name,age):
        self.__name = name
        self.__age = age
    @staticmethod
    def static_func():
        print("正在调用静态方法")
静态方法不像前两个有特定参数,比较自由,即使你强行把第一个参数名写出self和cls,也不会有相应的作用。静态方法可以通过类或者实例对象调用。
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._x
    def setx(self, value): self._x = value + 1
    def delx(self): del self._x
    x = 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
    @property
    def name(self):
        print("getter方法...")
        return self.__name
    @name.setter
    def name(self,value):
        print("setter方法...")
        self.__name = value
    @name.deleter
    def name(self):
        print("deleter方法...")
        del self.__name
p = Hero("蝙蝠侠")
p.name                      # getter方法...
p.name = "游城十代"          # setter方法...
del p.name                  # deleter方法...
一般@property只用于私有属性“公有化”,并且getter方法和deleter方法只能有self参数。
类的特殊成员
__doc__
表示类的描述信息
class Foo(object):
    '''Foo的描述'''
print(Foo.__doc__)					# Foo的描述
__module__和__class__
- __module__表示当前操作的对象在那个模块
- __class__表示当前操作的对象的类是什么,也就是谁创建了这个类,- metaclass还是- type
class Foo(object):
    pass
print(Foo.__module__)					# __main__
print(Foo.__class__)					# <class 'type'>
__all__
__all__是一个字符串list,用来定义模块中对于from XXX import *时要对外导出的符号,即要暴露的借口,但它只对import *起作用,对from XXX import XXX不起作用
__all__ = ['MNIST', 'FashionMNIST', 'CIFAR10', 'CIFAR100',
           'ImageRecordDataset', 'ImageFolderDataset']
__init__
构造方法,创建对象时自动执行的初始化函数
class Foo(object):
    def __init__(self):
        print("init方法...")
f = Foo()  # init方法...
__del__
- 析构方法,当对象在内存中被释放时,自动触发执行
- 此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
class Foo:
    def__del__(self):
        pass
__call__
- 对象后面加括号,触发执行
- 构造方法的执行是由创建对象触发的,即:对象 = 类名();而对于__call__方法的执行是由对象后加括号触发的,即:对象()或者类()()
class Demo(object):
    def __init__(self):
        print("执行init...")
    def __call__(self, *args, **kwargs):
        print("执行call...")
d = Demo()  # 执行init...
d()  # 执行call...
__dict__
- 返回类或对象中的所有成员
- 普通字段属于对象,静态字段和方法属于类
class Province(object):
    country = 'China'
    def __init__(self, name, count):
        self.name = name
        self.count = count
    def 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}
'''
__str__和__repr__
- 都是更改类的显示方式
- __str__是给用户看到的字符串,- __repr__是给开发者看的,比如debug时,变量列表显示的是- __repr__函数返回的内容
- 一般情况下只用写一个__str__,然后__repr__ = __str__
class Text(object):
    def __init__(self,text):
        self.__text = text
    def __str__(self):
        return self.__text
    __repr__ = __str__  # 一般都这么写偷懒
t = Text("无极剑圣")
print(t)  # 无极剑圣
__getitem__、__setitem__和__delitem__
- 用于索引操作,如字典。以上分别表示获取、设置、删除数据
- __getitem__也可以传入切片
class Subject(object):
    def __getitem__(self, item):  # 根据item返回一个值
        print("调用getitem...")
    def __setitem__(self, key, value):  # 设置key对应的值为value
        print("调用setitem...")
    def __delitem__(self, key):	  # 删除这组键值对
        print("调用delitem...")
m = Subject()
n = m['math']  # 调用getitem...
m['math'] = 100  # 调用setitem...
del m['math']  # 调用delitem...
__iter__和__next__
用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了__iter__和__next__
class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1  # 初始化两个计数器a,b
    def __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  # 返回下一个值
__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即可
创建对象
传统创建类
class Demo(object):
    def __init__(self,name):
        self.__name = name
d = 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类的构造方法创建。
type创建类
- 语法:type('类名',父类的元组,成员字典)
def init(self,name):
    self.__name = name
def 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时,注意元组单元素时的逗号,成员字典中,成员名是字符串,对应的值可以是方法地址,可以是属性值
init,show这些方法可以在前面加 @classmethod 或者 @staticmethod 等,来定义类函数和静态函数
__new__方法
__new__方法是类自带的一个方法,可以重写,__new__方法在实例化的时候也会执行,并且先于__init__方法之前执行,简单理解,创建对象和初始化对象。
class Foo(object):
    def __init__(self, name):
        self.name = name
        print("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__
"""
重写__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 ret
print(id(A))
a = A()
'''
输出:
	2151279654824
	2151279654824
	这是__new__()方法...
	<__main__.A object at 0x000001F4E4406FD0>
	<__main__.A object at 0x000001F4E4406FD0>
	这是__init__()方法...
'''
单例模式
class Number(object):
    __instance = None
    def __new__(cls, val):
        if cls.__instance is None:
            cls.__instance = super().__new__(cls)
            cls.__instance.__value = val
            return cls.__instance
        else:
            return cls.__instance
    def __str__(self):
        return str(self.__value)
m = Number(-3)
n = Number(-6)
print(m)  # -3
print(n)  # -3
print(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』面向对象(一)的更多相关文章
- 『Python』面向对象(二)
		继承 继承的语法 class Animal(object): def __init__(self,name): self.__name = name class Dog(Animal): kind = ... 
- 『Python』__getattr__()特殊方法
		self的认识 & __getattr__()特殊方法 将字典调用方式改为通过属性查询的一个小class, class Dict(dict): def __init__(self, **kw) ... 
- 『GoLang』面向对象
		我们总结一下前面看到的:Go 没有类,而是松耦合的类型.方法对接口的实现. 面向对象语言最重要的三个方面分别是:封装,继承和多态,在 Go 中它们是怎样表现的呢? Go实现面向对象的两个关键是stru ... 
- 『Python』 ThreadPool 线程池模板
		Python 的 简单多线程实现 用 dummy 模块 一句话就可以搞定,但需要对线程,队列做进一步的操作,最好自己写个线程池类来实现. Code: # coding:utf-8 # version: ... 
- 『Python』Python 调用 ZoomEye API 批量获取目标网站IP
		#### 20160712 更新 原API的访问方式是以 HTTP 的方式访问的,根据官网最新文档,现在已经修改成 HTTPS 方式,测试可以正常使用API了. 0x 00 前言 ZoomEye 的 ... 
- 『Python』为什么调用函数会令引用计数+2
		一.问题描述 Python中的垃圾回收是以引用计数为主,分代收集为辅,引用计数的缺陷是循环引用的问题.在Python中,如果一个对象的引用数为0,Python虚拟机就会回收这个对象的内存. sys.g ... 
- 『Python』库安装
		1.安装指定版本的tensorflow 虽然官网有4种安装方式,并且推荐用anaconda的方式,但是有时候我们需要指定版本的tensorflow,而pip可以做到. 比如我装的是anaconda3. ... 
- 『Python』装饰器
		一.参考 作者:zhijun liu 链接:https://www.zhihu.com/question/26930016/answer/99243411 来源:知乎 建议大家去原答案浏览 二.装饰器 ... 
- 『Python』多进程处理
		尝试学习python的多进程模组,对比多线程,大概的区别在: 1.多进程的处理速度更快 2.多进程的各个子进程之间交换数据很不方便 多进程调用方式 进程基本使用multicore() 进程池优化进程的 ... 
随机推荐
- redis如何实现分布式锁?
			1.使用redis中的自增来实现 2.使用setnx + del # 如果不存在set(返回1),如果存在则失败(返回0) 为了避免死锁会加上一个过期时间 自增方式 boolean isSelf = ... 
- 神州战神U盘安装windows10系统,启动项制作好后,在bios中识别不到自己的u盘问题
			我笔记本是神州战神,启动盘做完了后,按快捷键F2,进入boot下进行设置,找了半天没找到自己的u盘,很郁闷,捣鼓了半天才搞定,以下是我修改之后的设置: 1.把Secure Boot设置为disable ... 
- 基础篇:一文讲懂树莓派命令行文本编辑工具Vim的使用
			简介 众所周知,在Linux系统下的命令行调试界面,经常会遇到需要文本编辑的情况,而树莓派官方系统默认自带了Nano编辑器,Nano的操作门槛更低,但却不如Vim编辑器方便.Vim编辑器是由早期在Li ... 
- open62541(opcua)传输延迟探索小记
			缘起 将open62541作为中间件使用代替自定义数据的RPC,client通过订阅valueChange来接收数据.使用时发现有一些问题: 前后两次产生的数据相同时,不会触发valueChange ... 
- Java 数组结构
			数组是最常见的一种数据结构,是相同类型的.用一个标识符封装到一起的基本类型数据序列或对象序列.可以用一个统一的数组名和下标来唯一确定数组中的元素.实质上数组是一个简单的线性序列,因此数组访问起来很快. ... 
- (5)air202读取串口数据并上传到阿里云显示
			一.首先进行云端设置 根据串口助手显示的信息,以及模块文档说明我们可以知道 其中red和ir是红光LED的原始数据, HR表示心率值, HRvalid是心率是否有效标识, SP02是血氧数值,,SPO ... 
- clickhouse物化视图
			今天来简单介绍一下clickhouse的物化视图 物化视图支持表引擎,数据保存形式由它的表引擎决定,创建物化视图的完整语法如下: create materialized view mv_log eng ... 
- 目录-理解ASP.NET Core
			<理解ASP.NET Core>基于.NET5进行整理,旨在帮助大家能够对ASP.NET Core框架有一个清晰的认识. 目录 [01] Startup [02] Middleware [ ... 
- Windows10下MySQL的安装
			简单介绍一下MySQL的安装 官方网址:https://www.mysql.com/downloads/ 1.找到MySQL Community Edition (GPL) (注:GPL版本为开源,非 ... 
- 《网页布局基础篇》HTML+CSS单列布局--水平居中,垂直居中,水平垂直居中
			https://blog.csdn.net/panlu666_pl/article/details/66480433 一.水平居中 子元素在父元素中水平居中 1.使用 text-align和inlin ... 
