Python类和对象

类与对象

类即类别、种类,是面向对象设计最重要的概念,对象是属性与方法的结合体,而类则是一系列对象相似的属性与方法的高度概括.即类是把相似事物进行抽象,而对象的一个具体的实例.

在Python里面,任何都是类,str等类型也是一种类.

在程序代码里面,我们必须对事物进行建模,抽象出它的属性(特征,比如美丑等等)与方法(动作等动态行为),然后才能建立一个类;有了一个类之后,我们才能建立相关的对象,即实例

class Dog(object):      ##通过class方法可以创建一个类
	def __init__(self, name, speice, master, age):  ##赋予该类属性
		self.name = name
		self.speice = speice
		self.master = master
		self.age = age

	def dog_bark(self):   ##赋予方法
		print("%s正在汪汪汪....." %self.name)

	def eat_food(self):  ##必须写self这种形参,关联类
		print("%s正在吃饭"%self.name)


dog_1 = Dog("旺财", "哈士奇", "Jack", 7)  ##实例化一个对象出来
print(dog_1.name)
dog_1.dog_bark()  ##通过"."来调方法和属性

旺财
旺财正在汪汪汪.....

print(dog_1.__dict__) ##可以打印出对象的属性字典,另外类里面并没有方法,它需要到类里面调用
print(Dog.__dict__)   ##打印Dog类的属性字典

{'name': '旺财', 'speice': '哈士奇', 'master': 'Jack', 'age': 7}
{'__module__': '__main__', '__init__': <function Dog.__init__ at 0x0000027E895CCF28>, 'dog_bark': <function Dog.dog_bark at 0x0000027E895D6048>, 'eat_food': <function Dog.eat_food at 0x0000027E895D60D0>, '__dict__': <attribute '__dict__' of 'Dog' objects>, '__weakref__': <attribute '__weakref__' of 'Dog' objects>, '__doc__': None}

dog_1.name = "二哈"
print(dog_1.__dict__)   ##可以这样修改属性值,而如果在类里面找不到就会向上找,但不会出类的范围;
						##"__dict__"方法是调出对象的属性字典
{'name': '二哈', 'speice': '哈士奇', 'master': 'Jack', 'age': 7} 

其他方法

#python为类内置的特殊属性
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类(在讲继承时会讲)
类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
类名.__dict__# 类的字典属性
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)

###__init__方法
# 1、该方法内可以有任意的python代码
# 2、一定不能有返回值
##当我们建立对象时,就会触发__init__方法

继承

继承是一种创建新类的方式,新类可以继承一种基类(父类),在Python里面可以继承多个类;新建的类又叫做派生类或子类.在Python里面class建立的类,也继承于一个叫做“object”的基类.我们可以使用继承来减少重用性,使得代码量减少.

class Create(object):     ##创建一个基类
	def __init__(self, name, speice, age):
		self.name = name
		self.speice = speice
		self.age = age

	def eat_food(self):
		print("%s is eating!"%self.name)

	def sleep(self):
		print("%s is sleeping!"%self.name)

class Dog(Create):  ##通过这个方式,继承基类

dog = Dog("二哈", "哈士奇", 5)
dog.sleep()

二哈 is sleeping!  
>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
(<class '__main__.ParentClass1'>,)
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

经典类和新式类

在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类;显式地声明继承object的类,以及该类的子类,都是新式类
在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类

派生

然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了

class Create(object):
	def __init__(self, name, speice, age):
		self.name = name
		self.speice = speice
		self.age = age

	def eat_food(self):
		print("%s is eating!"%self.name)

	def sleep(self):
		print("%s is sleeping!"%self.name)

class Dog(Create):
	def __init__(self, name, master, age):  ##覆盖了原来的
		self.name = name 
		self.master = master
		self.age = age

	def eat_food(self):
		print("%s的狗狗正在吃饭" %self.master)

dog = Dog("金毛", "Hermaeus", 15)
dog.eat_food()
print(dog.master)

组合

在一个类中以另外一个类的对象作为数据属性,称为类的组合

class Hand(object):
	pass

class Head(object):
	pass

class Body(object):
	pass

class People(object):
	def __init__(self):
		self.hand = Hand()  ##即把其他类导入进来
		self.head = Head()
		self.body = Body()

接口、归一化设计与抽象类

接口指的是自己提供给使用者来调用自己功能的方式\方法\入口

接口提取了一群类共同的函数,可以把接口当做一个函数的集合;然后让子类去实现接口中的函数。

这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。

归一化的好处在于:

  1. 归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
  2. 归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合

抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化

import abc   ##调用该方法实现抽象类
class All_file(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def read(self):
        pass

    @abc.abstractmethod
    def write(self):
        pass

class Disk(All_file):
    def read(self):
        print('disk read')

    def write(self):
        print('disk write')

class Cdrom(All_file):
    def read(self):
        print('cdrom read')

    def write(self):
        print('cdrom write')

class Mem(All_file):
    def read(self):
        print('mem read')

    def write(self):
        print('mem write')

m1=Mem()
m1.read()
m1.write()

mem read
mem write

继承实现的原理

当类是经典类时会遵循深度优先;是新类是会遵循广度优先

对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表

class A(object):
    def test(self):
        print('from A')

class B(A):
    def test(self):
        print('from B')

class C(A):
    def test(self):
        print('from C')

class D(B):
    def test(self):
        print('from D')

class E(C):
    def test(self):
        print('from E')

class F(D,E):
    pass


print(F.__mro__)

那么就会产生一个和下列一样的MRO列表,查找的顺序是按着从左到右实现的,而子类会先于父类被检查;如果有多个父类会根据它们在列表中的顺序被检查;当存在两个合法的选择,选择第一个父类

(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

子类中调用父类的方法

指名道姓

class Create(object):
	def __init__(self, name, speice, age):
		self.name = name
		self.speice = speice
		self.age = age

	def eat_food(self):
		print("%s is eating!"%self.name)

	def sleep(self):
		print("%s is sleeping!"%self.name)

class Dog(Create):
	def __init__(self, name, speice, age, master):
		Create.__init__(self, name, speice, age)    ##直接通过名字引用,需要self参数
		self.master = master

	def eat_food(self):
		print("%s的狗狗正在吃饭" %self.master)


dog = Dog("二哈", "哈士奇", 18, "Hermaeus")
dog.sleep()

super()方法

class Create(object):
	def __init__(self, name, speice, age):
		self.name = name
		self.speice = speice
		self.age = age

	def eat_food(self):
		print("%s is eating!"%self.name)

	def sleep(self):
		print("%s is sleeping!"%self.name)

class Dog(Create):
	def __init__(self, name, speice, age, master):
		super().__init__(name, speice, age)    ##通过super()来调用,但是绝对不能有self
		self.master = master

	def eat_food(self):
		print("%s的狗狗正在吃饭" %self.master)


dog = Dog("二哈", "哈士奇", 18, "Hermaeus")
dog.sleep()

多态与多态性

在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数;分为静态多态动态多态.

>>> class Cat(Animal): #属于动物的另外一种形态:猫
...     def talk(self):
...         print('say miao')
... 
>>> def func(animal): #对于使用者来说,自己的代码根本无需改动
...     animal.talk()
... 
>>> cat1=Cat() #实例出一只猫
>>> func(cat1) #甚至连调用方式也无需改变,就能调用猫的talk功能
say miao

封装

单下划线开头

#其实这仅仅这是一种变形操作且仅仅只在类定义阶段发生变形
#类中所有单下划线开头的名称如_x在约定上被认为是封装,让人们在使用的过程中不要调用,但是实际上没有任何用

class A:
    _N=0 #约定不要使用
    def __init__(self):
        self._X=10 
    def _foo(self):
        print('from A')
    def bar(self):
        self._foo() 

#这种,在外部是可以通过_x这个名字访问到

双下划线开头

#其实这仅仅这是一种变形操作且仅仅只在类定义阶段发生变形
#类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式:

class A:
    __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
    def __init__(self):
        self.__X=10 #变形为self._A__X
    def __foo(self): #变形为_A__foo
        print('from A')
    def bar(self):
        self.__foo() #只有在类内部才可以通过__foo的形式访问到.

#A._A__N是可以访问到的,
#这种,在外部是无法通过__x这个名字访问到

封装的真谛在于明确地区分内外,封装的属性可以直接在内部使用,而不能被外部直接使用,然而定义属性的目的终归是要用,外部要想用类隐藏的属性,需要我们为其开辟接口,让外部能够间接地用到我们隐藏起来的属性

封装数据:将数据隐藏起来这不是目的.隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制

封装方法:目的是隔离复杂度,让程序的一些细节不需要被使用者知道

在导入模块的过程中,from module import *时不能被导入,但是你from module import _private_module依然是可以导入的

特性(property)

property是一种特殊的装饰器,使用它时会执行一段功能(函数)然后返回值

class People:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height=height
        
    @property
    def bmi(self):
        return self.weight / (self.height**2)

p1=People('Hermaeus',60,1.7)
print(p1.bmi)

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

绑定方法(classmethod)

  1. 绑定到类的方法:用classmethod装饰器装饰的方法

    为类量身定制,自动将类当作第一个参数传入(其实对象也可调用,但仍将类当作第一个参数传入)

  2. 绑定到对象的方法:没有被任何装饰器装饰的方法

    为对象量身定制,自动将对象当作第一个参数传入(属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)

class Room(object):
    tag=1
    def __init__(self,name,owner,width,length,heigh):
        self.name=name
        self.owner=owner
        self.width=width
        self.length=length
        self.heigh=heigh

    @classmethod
    def tell_info(cls,x):
        print(cls)
        print('-->',cls.tag,x)

Room.tell_info(10)

<class '__main__.Room'>
--> 1 10

非绑定方法(staticmethod)

不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说;就是一个普通工具而已

class Room(object):
    tag=1
    def __init__(self,name,owner,width,length,heigh):
        self.name=name
        self.owner=owner
        self.width=width
        self.length=length
        self.heigh=heigh

    @staticmethod    ##变成一个独立的方法
    def get_area(x, y):
    	res = x*y
    	print("Area is %s" %res)
        
Room.get_area(2, 3)

Area is 6

isinstance(obj,cls)和issubclass(sub,super)

isinstance(obj,cls)判断obj是否是类cls的对象

issubclass(sub,super)判断sub类是否是super类的派生类

############
class Foo(object):
	pass

obj = Foo()

print(isinstance(obj, Foo))

############
class Func(object):
	pass

class Func1(Func):
	pass

print(issubclass(Func1, Func))

True
True

反射

通过字符串的形式操作对象相关的属性.Python中的一切事物都是对象(都可以使用反射)

class Dog(object):
	def __init__(self, name, master, species):
		self.name = name
		self.master = master
		self.species = species

	def dog_bark(self):
		print("%s is barking!" %self.name)

	def eat_food(self):
		print("%s is eating!" %self.name)

dog = Dog("二哈", "Hermaeus", "哈士奇")

#####hasattr(object, attr)#########
##判断在对象里是否有某属性
print(hasattr(dog, "name"))

True

#####getattr(object, name, default=None)#########
##得到对象里的某属性,如果没有返回default
print(getattr(dog, "name"))
print(getattr(dog, "afjaljf", "没有"))

二哈
没有

#####getattrsetattr(x, y, v)#########
##设置属性
setattr(dog, "hobby", "拆家")
print(dog.__dict__)

{'name': '二哈', 'master': 'Hermaeus', 'species': '哈士奇', 'hobby': '拆家'}

#####delattr(x, y)#########
##删除某属性
delattr(dog, "name")
print(dog.__dict__)

{'master': 'Hermaeus', 'species': '哈士奇', 'hobby': '拆家'}

如果需要使用该方法的对象文件就是自己,那么可以调用sys模块里面的modules方法

import sys

def func1():
	print("From func1")

def func2():
	print("From func2")

this_module = sys.modules[__name__]

print(hasattr(this_module, "func1"))

True

动态导入模块

import importlib

importlib.import_module("名字")

__setattr__,__delattr__,__getattr__

class Foo(object):
    x = 1
    def __init__(self, y):
        self.y = y

    def __getattr__(self, item):
        print("From __getattr__")

    def __setattr__(self, key, value):
        print("From __getattr__")

    def __delattr__(self, item):
        print("From __delattr")

##__setattr__添加/修改属性会触发它的执行
## 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,那么根本没赋值,除非你直接操作属性字典,否则永远无法赋值
##self.__dict__[key] = value  ===> 会把值直接添加到对象的属性字典

##__delattr__删除属性的时候会触发
##self.__dict__.pop(item)  ===> 直接删除对象字典里的值

##__getattr__只有在使用点调用属性且属性不存在的时候才会触发

二次加工标准类型通过

通过继承派生的手段,基于标准数据类型来定制我们自己的数据类型,新增或者改写方法

class List(list):
    def append(self,p_object):
        if isinstance(p_object, int):
            raise TypeError("不支持int类型")  #raise方法可以抛出错误
        super().append(p_object)

s1 = List(("a", 'b', 'c'))
print(s1)
s1.append(12)  ##报错

…………………………………………………………………………………………
    raise TypeError("不支持int类型")
TypeError: 不支持int类型

授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能.其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性.而实现授权的关键点就是覆盖__getattr__方法

import time
class HandleFile(object):
    def __init__(self, filename, mode, encoding = "UTF-8"):
        self.file = open(filename, mode)

    def write(self, context):
        t = time.strftime("%Y-%m-%d %T")
        self.file.write("%s %s" %(t, context))

    def __getattr__(self, item):   ##覆盖了原来的方法,使得没有的方法到open里面找
        return getattr(self.file, item)  


f1 = HandleFile("b.txt", "w")
f1.write("Hello, World!")
f1.close()

__getattribute__

当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattr__(self, item):
        print('执行的是我')
        
    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')
        raise AttributeError('哈哈') ##会执行一次__getattr__

f1=Foo(10)
f1.x
f1.xxxxxx

描述符(__get__,__set__,__delete__)

描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发

而其又被分为:

数据描述符:至少实现了__get__()和__set__()

非数据描述符:没有实现__set__()

class Str(object):
    def __get__(self):
        print("From __get__")

    def __set__(self, instance, value):
        print("From __set__")

    def __delete__(self, instance):
        print("From __delete__")

class People(object):
    name = Str()  ##在其他类里面被当做属性时,才有被调用,name被Str类代理了
    def __init__(self, name):
        self.name = name

p1 = People("qq")

From __set__  ##触发了Str类里面的__set__

注意事项:

  • 描述符本身应该定义成新式类,被代理的类也应该是新式类

  • 必须把描述符定义成这个类的类属性,不能为定义到构造函数中

  • 要严格遵循该优先级,优先级由高到底分别是

    • 类属性

    • 数据描述符

    • 实例属性

    • 非数据描述符

    • 找不到的属性触发__getattr__()

property另外用法

一个静态属性property本质就是实现了get,set,delete三种方法

class Foo:
    @property
    def AAA(self):
        print('get的时候运行我啊')

    @AAA.setter
    def AAA(self,value):
        print('set的时候运行我啊')

    @AAA.deleter
    def AAA(self):
        print('delete的时候运行我啊')
        
    ##AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应

#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

__setitem__,__getitem__,__delitem__

class Foo(object):

    def __getitem__(self, key):
        print('__getitem__', key)

    def __setitem__(self, key, value):
        print('__setitem__', key, value)

    def __delitem__(self, key):
        print('__delitem__', key)

obj = Foo()
##类字典的操作将触发这些
result = obj['k1']  # 自动触发执行 __getitem__
obj['k2'] = 'Hermaeus'  # 自动触发执行 __setitem__
del obj['k1']  # 自动触发执行 __delitem__

__str__,__repr__,__format__

str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常


formated_time = {
    "YY":"{obj.year}-{obj.month}-{obj.date}",
    "MM":"{obj.year}/{obj.month}/{obj.date}",
    "DD": "{obj.year}:{obj.month}:{obj.date}"
}

class Time(object):
    def __init__(self, year, month, date):
        self.year = year
        self.month = month
        self.date = date

    def __str__(self):
        return "From __str__"

    def __repr__(self):
        return  "From __repr__"

    def __format__(self, format_spec):
        if not format_spec or format_spec not in formated_time.keys():
            format_spec = "DD"
        res_spec = formated_time[format_spec]
        return res_spec.format(obj = self)

try_time = Time(2018, 5, 23)
print(try_time)
print(try_time.__format__("YY"))

From __str__
2018-5-23

__slots__

__slots__是一个类变量,其中变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性);使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的);字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__

class Foo(object):
    __slots__ = ["name", "age"]

f1 = Foo()
f1.name = "Hermaeus"
f1.age = 19
print(f1.__dir__())
print(f1.__slots__)

##对象里面没有了__dict__,只有了__slots__,这样有利于减少内存负担
['__module__', '__slots__', 'age', 'name', '__doc__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__init__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']  
['name', 'age']

__next__和__iter__实现迭代器协议

##有了__next__和__iter__,便是一个迭代器了
class func(object):
    def __init__(self, x):
        self.x = x

    def __iter__(self):
        return self

    def __next__(self):
        self.x += 1
        return self.x

test_func = func(2)  
print(test_func.__next__())
print(test_func.__next__())
print(test_func.__next__())
print(test_func.__next__())

实现斐波那契数列

class Fib(object):
    def __init__(self):
        self.a = 0
        self.b = 1

    def __iter__(self):
        return self

    def __next__(self):
        self.a , self.b = self.b , self.a + self.b
        return self.a

f1 = Fib()
print(f1.__next__())
print(f1.__next__())
print(f1.__next__())

__doc__,__module__和__class__

class Func(object):
    """  The Infomation of Func"""
    pass
print(Func.__doc__)  ##获取备注信息

from lib.aa import C
obj = C()
print obj.__module__  ##得到是那个模块里的
print obj.__class__   ##得到是那个类里的

The Infomation of Func
lib.aa   即:输出模块
lib.aa.C  即:输出类

__del__

析构方法,当对象在内存中被释放时,自动触发执行

class Foo(object):
    def __del__(self):
        print("Over")

f1 = Foo()  ##不过怎么样,只要一释放内存,就会触发__del__

__enter__和__exit__

”with open(文件) as f“,又叫做上下文管理协议,为了让一个对象能使用with语句,必须在对象的类里面申明__enter和__exit__方法

class MyOpen(object):
    def __init__(self, filename, mode, encoding = "UTF-8"):
        self.filename = filename
        self.mode = mode
        self.encoding = encoding

    def __enter__(self): ##出现with,则触发__enter__,有返回值就赋予给as申明了的变量
        self.f = open(self.filename, self.mode, encoding = self.encoding)
        return self.f

    def __exit__(self, exc_type, exc_val, exc_tb):  ##with执行完之后,触发__exit__
        self.f.close()  ##exc_type ==>异常类型  exc_val ==>异常值  exc_tb ==>追溯信息
        return True      

    def __getattr__(self, item):  ##避免找不到指定方法而触发默认的__getattr__
        return self, item

with MyOpen("b.txt", "r") as f:
    print(f.read())

__call__

在对象后面加括号执行

class Foo(object):
    def __call__(self, *args, **kwargs):
        print("Come from __call__")
f1 = Foo()   ##不能直接Foo(),不会执行
f1()

Come from __call__

元类

在Python里面一切皆对象,那么类调用的类我们就叫做元类,那么默认的元类是type

class Foo(object):
    pass

print(type(Foo))

可以直接使用type建立新类

##type(name, bases, dict) -> a new type

def __init__(self, name, age):
    self.name = name
    self.age = age

Foo = type("Foo", (object,), {"x":11, "__init__":__init__})
f1 = Foo("Hermaeus", 19)
print(f1.__dict__)

{'name': 'Hermaeus', 'age': 19}

自己建立元类

class MyType(type):
    def __init__(cls, x, y, z):
        print("Runing....")
    def __call__(self, *args, **kwargs):
        obj = self.__new__(self)  ##建立一个空的对象
        self.__init__(obj, *args, **kwargs)  ##重新使用type
        return obj

class Foo(metaclass=MyType):  ## "metaclass="指定元类
    def __init__(self, name):
        self.name = name

f1 = Foo("Hermaeus")
print(f1.name)

Runing....
Hermaeus
posted @ 2019-04-18 20:55  Mingle_Yuan  阅读(230)  评论(0编辑  收藏  举报