python面向对象编程
文章目录
- 面向对象编程概述
- 类的创建
- 类的封装
- 类的继承
- 访问限制
- 获取对象信息
- 多重继承
- 类的属性
- 类的方法
-
- `__init__`
- `__del__`
- `__doc__ `
- `__str__`
- `__call__`
- `__class__`
- `__module__ `
- `__dict__ `
- `__iter__`
- `__next__`
- `__getitem__`
- `__setitem__` 和` __delitem__`
- `__getattr__`
- `__add__`; `__sub__` ; `__mul__` ;`__truediv__` ; `__mod__`
- `__cmp__`
- `__lt__`; `__le__`;` __gt__ `; `__ge__`;`__eq__`;`__ne__`
- `__repr__`
- `__bool__`
- @staticmethod
- @classmethod
- @property
- `__new__ `
- 单例设计模式
- metaclass
面向对象编程概述
面向过程–怎么做?
- 把完成某一个需求的所有步骤,从头到尾逐步实现
- 根据开发需求,将某些功能独立的代码封装成一个又一个函数
- 最后完成的代码,就是顺序地调用不同的函数
特点:
- 注重步骤与过程,不注重职责分工
- 如果需求复杂,代码就会变得很复杂
- 开发复杂项目,没有固定的套路,开发难度很大!
面向对象–谁来做?
面向对象程序设计(object oriented programming)简称OOP。面向对象编程,使得数据的管理更加合理化和自动化,减少程序出错,少写代码,程序更加容易维护。相比较函数,面向对象是更大的封装,根据职责在一个对象中封装多个方法
1 在完成某个需求前,首先确定职责–要做的事情(方法)
2 根据职责确定不同的对象,在对象内部封装不同的方法(多个)
3 最后完成的代码,就是顺序地让不同的对象调用不同的方法
万物皆对象
类和对象是面向对象编程的两个核心概念
对象优越性
- 封装:对象将属性和方法进行封装,可以实现信息隐蔽化,用户仅能够使用对象的方法而不能修改这些方法和属性。
- 继承:获取父对象的属性和能力,再加上自定义的属性和能力,实现代码的重用
- 包含:一个对象划分几部分,分别进行建模,最后组装成一个完整的对象
- 多态性:不同对象对同一方法响应不同的行动
类和对象
类和对象是面向对象编程的两个核心概念
类 是对一群具有相同特征或者行为的事物的一个统称,是抽象的,不能直接使用
特征 被称为 属性
行为 被称为 方法
类(class)就相当于制造飞机的图纸,就是一个模板 ,是负责创建对象的
对象(object) 是由类创建出来的一个具体存在,可以直接使用
对象 (object)就是相当于用 图纸 制造的飞机
类的创建
三要素:
- 类名 ,满足大驼峰命名法
- 属性:特征
- 方法:行为
大驼峰命名法:1 每个单词的首字母大写;2 单词与单词之间没有下划线 例如CapWords
-
创建出来的 对象 叫做 类 的 实例
-
创建对象的 动作 叫做 实例化
-
对象的属性 叫做 实例属性
-
对象调用的方法 叫做 实例方法
-
类的属性 叫做 类属性
普通方式
class Person(object):
pass
class后面紧接着是类名,即Person,类名通常是大写开头的单词,满足大驼峰命名法,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类
定义好了Person类,就可以根据Person类创建出Person的实例(instance),创建实例是通过类名+()实现的:
这里实例(instance) 和 对象(object)是一样一样的
tony = Person()
print(tony)
print(Person)
<__main__.Person object at 0x7f5748036470>
<class '__main__.Person'>
可以看到,变量tony指向的就是一个Person的实例,后面的0x7f574891ff98是内存地址,每个object的地址都不一样,而Person本身则是一个类。
给一个实例变量绑定属性,比如,给实例tony绑定一个sex属性:
tony.sex = 'man'
print(tony.sex)
man
由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__
方法,在创建实例的时候,就把name,age等属性绑上去:
提到的第一个方法__init__
,特殊方法,前后各有两个下划线
class Person(object):
def __init__(self, name, weight):
self.name = name
self.weight = weight
注意到__init__
方法的第一个参数永远是self
,表示创建的实例本身,而非类,因此,在__init__
方法内部,就可以把各种属性绑定到self
,因为self
就指向创建的实例本身
richard = Person("理查德", 60)
print(richard.name)
print(richard.weight)
理查德
60
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self
,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数
特殊方式
def run(self):
print("%s 爱跑步,跑步锻炼身体" % self.name)
def eat(self):
print("%s 是吃货,吃完这顿再减肥" % self.name)
self.weight += 1
def __init__(self, name, weight):
self.name = name
self.weight = weight
Person = type('Person', (object,), {'run':run, 'eat':eat, '__init__':__init__})
mary = Person("mary",70)
mary.eat()
mary 是吃货,吃完这顿再减肥
- type第一个参数:类名
- type第二个参数:当前类的基类
- type第三个参数:类的成员
类的封装
对象方法的细节都被封装在类的内部,调用很容易,但却不用知道内部实现的细节
多个对象属性之间互不干扰
class Person(object):
def __init__(self, name, weight):
self.name = name
self.weight = weight
def __str__(self):
return "我的名字叫 %s 体重是 %.2f 公斤" % (self.name, self.weight)
def run(self):
print("%s 爱跑步,跑步锻炼身体" % self.name)
self.weight -= 0.5
def eat(self):
print("%s 是吃货,吃完这顿再减肥" % self.name)
self.weight += 1
这里又提到一个特殊方法__str__
,如果一个类中定义了__str__
方法,那么在打印 对象 时,默认输出该方法的返回值
nina = Person("nina",55)
print(nina)
nina.run()
print(nina)
我的名字叫 nina 体重是 55.00 公斤
nina 爱跑步,跑步锻炼身体
我的名字叫 nina 体重是 54.50 公斤
类的继承
继承概念:
子类 拥有 父类 的所有 方法 和 属性
继承语法:
class 类名(父类名):
pass
实例演示
- 不使用继承开发
class Animal(object):
def eat(self):
print("吃")
def drink(self):
print("喝")
def run(self):
print("跑")
def sleep(self):
print("睡")
class Dog(object):
def eat(self):
print("吃")
def drink(self):
print("喝")
def run(self):
print("跑")
def sleep(self):
print("睡")
def bark(self):
print("汪汪叫")
wangcai = Dog()
wangcai.eat()
wangcai.drink()
wangcai.run()
wangcai.sleep()
wangcai.bark()
吃
喝
跑
睡
汪汪叫
- 使用继承开发,修改上面的例子
class Animal(object):
def eat(self):
print("吃")
def drink(self):
print("喝")
def run(self):
print("跑")
def sleep(self):
print("睡")
class Dog(Animal):
def bark(self):
print("汪汪叫")
wangcai = Dog()
wangcai.eat()
wangcai.drink()
wangcai.run()
wangcai.sleep()
wangcai.bark()
吃
喝
跑
睡
汪汪叫
专业术语:
- Dog 类 是 Animal 类的 子类 ,Animal类是Dog类的 父类,Dog类 从 Aniaml类继承
- Dog 类 是 Animal 类的 派生类,Animal类是Dog类的 基类,Dog类 从 Aniaml类派生
继承的传递性
class Animal(object):
def eat(self):
print("吃")
def drink(self):
print("喝")
def run(self):
print("跑")
def sleep(self):
print("睡")
class Dog(Animal):
def bark(self):
print("汪汪叫")
class XiaoTianQuan(Dog):
def fly(self):
print("我会飞")
xtq = XiaoTianQuan()
xtq.fly()
xtq.bark()
xtq.eat()
我会飞
汪汪叫
吃
class Animal(object):
def eat(self):
print("吃")
def drink(self):
print("喝")
def run(self):
print("跑")
def sleep(self):
print("睡")
class Dog(Animal):
def bark(self):
print("汪汪叫")
class XiaoTianQuan(Dog):
def fly(self):
print("我会飞")
class Cat(Animal):
def catch(self):
print("抓老鼠")
xtq = XiaoTianQuan()
xtq.fly()
xtq.bark()
xtq.eat()
xtq.catch()
我会飞
汪汪叫
吃
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-45-c9f717720499> in <module>
36 xtq.eat()
37
---> 38 xtq.catch()
AttributeError: 'XiaoTianQuan' object has no attribute 'catch'
覆盖父类的方法
如果子类中,重写了父类的方法
在使用子类对象调用方法时,会调用子类中重写的方法
class Dog(Animal):
def bark(self):
print("汪汪叫")
class XiaoTianQuan(Dog):
def fly(self):
print("我会飞")
def bark(self):
print("叫得跟神一样...")
xtq = XiaoTianQuan()
xtq.bark()
叫得跟神一样...
多态
不同对象对同一方法响应不同的行动
class Animal(object):
def __init__(self, name):
self.name = name
def talk(self):
pass
@staticmethod
def animal_talk(obj):
obj.talk()
class Cat(Animal):
def talk(self):
print('miao miao')
class Dog(Animal):
def talk(self):
print('wang wang')
d = Dog("哮天犬")
d.talk()
c = Cat("加菲")
c.talk()
# def animal_talk(obj):
# obj.talk()
Animal.animal_talk(c)
Animal.animal_talk(d)
e= Animal('xxx')
e.animal_talk(c)
e.animal_talk(d)
wang wang
miao miao
miao miao
wang wang
miao miao
wang wang
对父类方法进行拓展
两种解决方法:
- 调用未绑定的父类方法(通过父类名调用父类的方法)
- 使用super().父类方法
import random as r
class Fish(Animal):
def __init__(self):
self.x = r.randint(0, 10)
self.y = r.randint(0, 10)
def move(self):
self.x -= 1
print('我的位置是:', self.x, self.y)
class Goldfish(Fish):
pass
class Carp(Fish):
pass
class Salmon(Fish):
pass
shark在保留自身的构造方法__init__
同时也继承父类构造方法
super() -> same as super(__class__, <first argument>)
class Shark(Fish):
def __init__(self):
self.hungry = True
# super().__init__()
super(Shark, self).__init__()
def eat(self):
if self.hungry:
print('吃货的梦想就是天天有的吃')
self.hungry = False
else:
print('太撑了,吃不下了')
shark = Shark()
shark.eat()
shark.move()
吃货的梦想就是天天有的吃
我的位置是: 6 7
class Shark(Fish):
def __init__(self):
self.hungry = True
Fish.__init__(self)
def eat(self):
if self.hungry:
print('吃货的梦想就是天天有的吃')
self.hungry = False
else:
print('太撑了,吃不下了')
shark = Shark()
shark.eat()
shark.move()
吃货的梦想就是天天有的吃
我的位置是: 0 2
访问限制
内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__
开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问
在Python中,变量名类似__xxx__
的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__
这样的变量名。
class Fish(Animal):
def __init__(self,name):
self._name = name
self.x = r.randint(0, 10)
self.y = r.randint(0, 10)
def _move(self):
self.x -= 1
print('我的位置不可知')
def move(self):
self.x -= 1
print('我的位置是:', self.x, self.y)
fish = Fish('美人鱼')
print(fish._name)
fish._move()
美人鱼
我的位置不可知
有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”
class Fish(Animal):
def __init__(self,name):
self.__name = name
self.x = r.randint(0, 10)
self.y = r.randint(0, 10)
def __move(self):
self.x -= 1
print('我的位置不可知')
def move(self):
self.x -= 1
print('我的位置是:', self.x, self.y)
fish = Fish('美人鱼')
print(fish.__name)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-116-6f92b2f7d0cd> in <module>
14
15 fish = Fish('美人鱼')
---> 16 print(fish.__name)
AttributeError: 'Fish' object has no attribute '__name'
双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Fish__name,所以,仍然可以通过_Fish__name来访问__name变量
print(fish._Fish__name)
fish._Fish__move()
美人鱼
我的位置不可知
但是如果外部代码要获取name怎么办?可以给Fish类增加get_name这样的方法:
class Fish(Animal):
def __init__(self,name):
self.__name = name
self.x = r.randint(0, 10)
self.y = r.randint(0, 10)
def get_name(self):
print('我是%s'% self.__name)
def move(self):
self.x -= 1
print('我的位置是:', self.x, self.y)
fish = Fish('美人鱼')
fish.get_name()
我是美人鱼
fish.get_name()
fish.__name = '人鱼小姐'
print(fish.__name)
我是美人鱼
人鱼小姐
表面上看,外部代码“成功”地设置了
__name
变量,但实际上这个__name
变量和class内部的__name
变量不是一个变量!内部的__name
变量已经被Python解释器自动改成了_Fish__name
,而外部代码给fish新增了一个__name
变量。不信试试
fish.get_name()
我是美人鱼
对父类的访问限制
class Fish(Animal):
def __init__(self,name):
self.__name = name
self.x = r.randint(0, 10)
self.y = r.randint(0, 10)
def get_name(self):
print('我是%s'% self.__name)
def __move(self):
self.x -= 1
print('我的位置不可知')
def move(self):
self.x -= 1
self.__move()
print('我的位置是:', self.x, self.y)
class Shark(Fish):
def eat(self):
self.__move()
fish = Shark("大白鲨")
fish.eat()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-121-859ae082132f> in <module>
23
24 fish = Shark("大白鲨")
---> 25 fish.eat()
<ipython-input-121-859ae082132f> in eat(self)
20
21 def eat(self):
---> 22 self.__move()
23
24 fish = Shark("大白鲨")
AttributeError: 'Shark' object has no attribute '_Shark__move'
在子类的对象方法中,不能调用父类的私有方法
class Shark(Fish):
def eat(self):
self.move()
fish = Shark("大白鲨")
fish.eat()
我的位置不可知
我的位置是: -2 10
在子类的对象方法中,调用父类的公有方法
获取对象信息
type()
基本类型都可以用type()判断:
print(type(123))
print(type('str'))
print(type(None))
<class 'int'>
<class 'str'>
<class 'NoneType'>
如果一个变量指向函数或者类,也可以用type()判断:
print(type(abs))
print(type(fish))
<class 'builtin_function_or_method'>
<class '__main__.Shark'>
但是type()
函数返回的是什么类型呢?它返回对应的Class类型。如果我们要在if语句中判断,就需要比较两个变量的type类型是否相同:
print(type(123)==type(456))
print(type(123)==int)
print(type(123)==str)
True
True
False
判断基本数据类型可以直接写int
,str
等,但如果要判断一个对象是否是函数怎么办?可以使用types
模块中定义的常量
import types
def fn():
pass
print(type(fn)==types.FunctionType)
print(type(abs)==types.BuiltinFunctionType)
print(type(lambda x: x)==types.LambdaType)
print(type((x for x in range(10)))==types.GeneratorType)
True
True
True
True
isinstance()
对于class的继承关系来说,使用type()
就很不方便。我们要判断class的类型,可以使用isinstance()
函数。
我们回顾上次的例子,如果继承关系是:
根类object---->Animal---->Fish---->Shark
根类object---->Animal---->Dog---->XiaoTianQuan
那么,isinstance()就可以告诉我们,一个对象是否是某种类型。先创建2种类型的对象:
a = Fish('x')
b = Shark('xx')
d = Dog("哮天犬")
然后,判断:
print(isinstance(b, Shark))
True
没有问题,因为b变量指向的就是Shark对象。
再判断:
print(isinstance(b, Fish))
True
b虽然自身是Shark类型,但由于Shark是从Fish继承下来的,所以,b也还是Fish类型。换句话说,isinstance()判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上。
因此,我们可以确信,b还是Animal类型:
print(isinstance(b, Animal))
print(isinstance(d, Animal))
True
True
print(isinstance(d, Dog))
print(isinstance(d, Fish))
True
False
能用type()
判断的基本类型也可以用isinstance()
判断:
print(isinstance('a', str))
print(isinstance(123, int))
True
True
并且还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是list或者tuple:
print(isinstance([1, 2, 3], (list, tuple)))
print(isinstance((1, 2, 3), (list, tuple)))
True
True
总是优先使用isinstance()判断类型,可以将指定类型及其子类“一网打尽”
dir()
如果要获得一个对象的所有属性和方法,可以使用dir()
函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:
print(dir('ABC'))
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
类似__xxx__
的属性和方法在Python中都是有特殊用途的,比如__len__
方法返回长度。在Python中,如果你调用len()
函数试图获取一个对象的长度,实际上,在len()
函数内部,它自动去调用该对象的__len__()
方法,所以,下面的代码是等价的:
print(len('ABC'))
print('ABC'.__len__())
3
3
我们自己写的类,如果也想用len(myObj)
的话,就自己写一个__len__()
方法:
class Mylen(str):
def __len__(self):
return 1
mylen = Mylen()
len(mylen)
1
反射
仅仅把属性和方法列出来是不够的,配合getattr()
、setattr()
,delattr()
以及hasattr()
,我们可以直接操作一个对象的状态
class MyObject(object):
def __init__(self):
self.x = 9
def power(self):
return self.x * self.x
obj = MyObject()
紧接着,可以测试该对象的属性:
print(hasattr(obj, 'x'))
print(hasattr(obj, 'y'))
setattr(obj, 'y', 19)
print(hasattr(obj, 'y'))
print(getattr(obj, 'y'))
print(obj.y)
delattr(obj, 'y')
print(getattr(obj, 'y'))
True
False
True
19
19
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-165-f0feb18a0533> in <module>
9
10 delattr(obj, 'y')
---> 11 print(getattr(obj, 'y'))
AttributeError: 'MyObject' object has no attribute 'y'
如果试图获取不存在的属性,会抛出AttributeError的错误:
getattr(obj, 'z')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-166-12ff9bb1afd4> in <module>
----> 1 getattr(obj, 'z')
AttributeError: 'MyObject' object has no attribute 'z'
可以传入一个default参数,如果属性不存在,就返回默认值:
print(getattr(obj, 'z', 404))
404
反射实例演示
def bulk(self):
print("%s is yelling...." %self.name)
class Dog(object):
def __init__(self,name):
self.name = name
def eat(self,food):
print("%s is eating..."%self.name,food)
d = Dog("xiaotianquan")
choice = input(">>:").strip()
if hasattr(d,choice):
func= getattr(d,choice) #获取成员对象方法的内存地址
func('apple')
else:
setattr(d,choice,bulk) #d.talk = bulk
func = getattr(d, choice)
func(d)
>>:talk
xiaotianquan is yelling....
多重继承
py3 经典类和新式类都是统一按广度优先来继承的
按广度,D继承B,没有再继承C,没有最后继承A
class A(object):
def __init__(self):
print("A")
class B(A):
def __init__(self):
print("B")
# def test(self):
# print("B --- test 方法")
#
# def demo(self):
# print("B --- demo 方法")
class C(A):
def __init__(self):
print("C")
def test(self):
print("C --- test 方法")
def demo(self):
print("C --- demo 方法")
class D(B, C):
"""多继承可以让子类对象,同时具有多个父类的属性和方法"""
pass
# 创建子类对象
d = D()
# 确定D类对象调用方法的顺序
print(D.__mro__)
B
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
d.test()
d.demo()
C --- test 方法
C --- demo 方法
py2 经典类是按深度优先来继承的,新式类是按广度优先来继承的
新式类 和 经典类 在多继承时 —— 会影响到方法的搜索顺序
为了保证编写的代码能够同时在 Python 2.x
和 Python 3.x
运行!
今后在定义类时,如果没有父类,建议统一继承自 object
类的属性
复习概念:
-
创建出来的 对象 叫做 类 的 实例
-
创建对象的 动作 叫做 实例化
-
对象的属性 叫做 实例属性, 定义时使用self.属性名
-
对象调用的方法 叫做 实例方法
-
类属性 就是针对 类对象 定义的属性, 同一个类的所有实例所共有的,引用时要使用 类名.类变量名
- 类属性 用于记录 与这个类相关 的特征
-
类方法 就是针对 类对象 定义的方法
- 在 类方法 内部可以直接访问 类属性 或者调用其他的 类方法
实例变量(实例属性)与类变量(类属性)的区别:
实例变量存在每个实例的内存里,每个实例之间相互不影响
类变量存在类的内存里,相当于所有的实例共享这个内存
实例演示
class Property(object):
class_name = 'Property'
def __init__(self, x=0):
self.x = x
print('初始化实例')
def class_info(self):
print("类变量值:", Property.class_name)
print('实例变量值:',self.x)
def change(self, x):
print('修改Property实例变量为', x)
self.x = x
def change_property(self, name):
print('修改Property类变量为', name)
Property.class_name = name
sun = Property()
初始化实例
sun.class_info()
类变量值: changed
实例变量值: 0
sun.change(3)
修改Property实例变量为 3
sun.class_info()
类变量值: changed
实例变量值: 3
sun.change_property("changed")
修改Property类变量为 changed
sun.class_info()
类变量值: changed
实例变量值: 3
假设类变量与实例变量重名,会出现什么情况?
class Property(object):
name = 'Property'
def __init__(self, x=0):
self.x = x
print('初始化实例')
def class_info(self):
print("类变量值:", Property.name)
print('实例变量值:',self.x)
def change(self, x):
print('修改Property实例变量为', x)
self.x = x
def change_property(self, name):
print('修改Property类变量为', name)
Property.name = name
p = Property()
初始化实例
打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
print(p.name)
Property
打印类的name属性
print(Property.name)
Property
给实例绑定name属性
p.name = 'richard'
由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
print(p.name)
richard
但是类属性并未消失,用Property.name仍然可以访问
print(Property.name)
Property
如果删除实例的name属性
del p.name
再次调用p.name,由于实例的name属性没有找到,类的name属性就显示出来了
print(p.name)
Property
在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性
实例练习
为了统计学生人数,可以给Student类增加一个类属性,每创建一个实例,该属性自动增加:
class Student(object):
count = 0
def __init__(self, name):
self.name = name
Student.count += 1
测试
if Student.count != 0:
print('测试失败!')
else:
bart = Student('Bart')
if Student.count != 1:
print('测试失败!')
else:
lisa = Student('Bart')
if Student.count != 2:
print('测试失败!')
else:
print('Students:', Student.count)
print('测试通过!')
Students: 2
测试通过!
类的方法
__init__
初始化方法(构造方法)
class Cat(object):
def __init__(self, new_name):
print("这是一个初始化方法")
self.name = new_name
tom = Cat("Tom")
这是一个初始化方法
__del__
析构方法
应用场景
__init__
改造初始化方法,可以让创建对象更加灵活__del__
如果希望在对象被销毁前,再做一些事情,可以考虑一下__del__
方法
class Cat(object):
def __init__(self, new_name):
self.name = new_name
print("%s 来了" % self.name)
def __del__(self):
print("%s 我去了" % self.name)
tom = Cat("Tom")
# del tom
Tom 来了
Tom 我去了
__doc__
表示类的描述信息
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self, new_name):
self.name = new_name
print("%s 来了" % self.name)
def __del__(self):
print("%s 我去了" % self.name)
print(Cat.__doc__)
描述类信息,这是一个喵喵
__str__
在 Python
中,使用 print
输出 对象变量,默认情况下,会输出这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中的地址(十六进制表示)
如果在开发中,希望使用 print 输出 对象变量 时,能够打印 自定义的内容,就可以利用 __str__
这个内置方法了
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self, new_name):
self.name = new_name
print("%s 来了" % self.name)
def __del__(self):
print("%s 我去了" % self.name)
def __str__(self):
# 必须返回一个字符串
return "我是小猫[%s]" % self.name
tom = Cat("Tom")
print(tom)
Tom 来了
Tom 我去了
我是小猫[Tom]
__call__
对象后面加括号,触发执行,可调用
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于
__call__
方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self, new_name):
self.name = new_name
print("%s 来了" % self.name)
def __del__(self):
print("%s 我去了" % self.name)
def __str__(self):
# 必须返回一个字符串
return "我是小猫[%s]" % self.name
def __call__(self):
print("执行__call__方法")
tom = Cat("Tom")
tom()
Tom 来了
Tom 我去了
执行__call__方法
__class__
表示当前操作的对象的类是什么
print(tom.__class__)
<class '__main__.Cat'>
__module__
表示当前操作的对象在那个模块
__main__
表示没有从别的模块中导入
print(tom.__module__)
__main__
lib = __import__('lib.test')
tom = lib.test.Person()
print(tom.__module__)
lib.test
__dict__
查看类或对象中的所有成员 ,输出结果是什么格式?
# 获取类的成员,即:静态字段、方法、
print(Cat.__dict__)
{'__module__': '__main__', '__doc__': '描述类信息,这是一个喵喵', '__init__': <function Cat.__init__ at 0x7f5733fbe620>, '__del__': <function Cat.__del__ at 0x7f5733fbe510>, '__str__': <function Cat.__str__ at 0x7f5733fbe1e0>, '__call__': <function Cat.__call__ at 0x7f5733fbe268>, '__dict__': <attribute '__dict__' of 'Cat' objects>, '__weakref__': <attribute '__weakref__' of 'Cat' objects>}
# 获取 对象tom 的成员
tom = Cat("Tom")
print(tom.__dict__)
Tom 来了
Tom 我去了
{'name': 'Tom'}
__iter__
如果类加上__iter__
, 实例化后的对象是一个可迭代对象
from collections import Iterable
print(isinstance('abc',Iterable))
print(isinstance([],Iterable))
print(isinstance({},Iterable))
True
True
True
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self, new_name):
self.name = new_name
print("%s 来了" % self.name)
def __del__(self):
print("%s 我去了" % self.name)
def __str__(self):
# 必须返回一个字符串
return "我是小猫[%s]" % self.name
def __call__(self):
print("执行__call__方法")
def __iter__(self):
return self
tom = Cat("Tom")
Tom 来了
Tom 我去了
print(isinstance(tom ,Iterable))
print(isinstance(tom ,Iterator))
True
False
__next__
把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__()
与 __next__()
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self, new_name):
self.name = new_name
self.num = 0
print("%s 来了" % self.name)
def __del__(self):
print("%s 我去了" % self.name)
def __str__(self):
# 必须返回一个字符串
return "我是小猫[%s]" % self.name
def __call__(self):
print("执行__call__方法")
def __iter__(self):
return self
def __next__(self):
self.num += 1
if self.num > 6:
raise StopIteration()
print('%s 产下了第%d个小猫'%(self.name,self.num))
tom = Cat("Tom")
Tom 来了
Tom 我去了
from collections import Iterable
from collections import Iterator
print(isinstance(tom ,Iterable))
print(isinstance(tom ,Iterator))
True
True
next(tom)
Tom 产下了第1个小猫
next(tom)
Tom 产下了第2个小猫
for i in tom:
pass
Tom 产下了第3个小猫
Tom 产下了第4个小猫
Tom 产下了第5个小猫
Tom 产下了第6个小猫
tom[0]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-299-fb6fc0384b04> in <module>
----> 1 tom[0]
TypeError: 'Cat' object does not support indexing
__getitem__
要表现得像list那样按照下标取出元素,需要实现__getitem__()
方法:
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self, new_name):
self.name = new_name
self.num = 0
print("%s 来了" % self.name)
def __del__(self):
print("%s 我去了" % self.name)
def __str__(self):
# 必须返回一个字符串
return "我是小猫[%s]" % self.name
def __call__(self):
print("执行__call__方法")
def __iter__(self):
return self
def __next__(self):
self.num += 1
if self.num > 6:
raise StopIteration()
print('%s 产下了第%d小猫'%(self.name,self.num))
def __getitem__(self, n):
a = "第1个小猫"
for x in range(n):
a = '第%d个小猫'% (x+2)
return a
tom = Cat("Tom")
Tom 来了
Tom 我去了
print(tom[0])
第1个小猫
print(tom[8])
第9个小猫
tom[0:2]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-305-ca1631d2d9b8> in <module>
----> 1 tom[0:2]
<ipython-input-301-07ecfbc21c8b> in __getitem__(self, n)
27 def __getitem__(self, n):
28 a = "第1个小猫"
---> 29 for x in range(n):
30 a = '第%d个小猫'% (x+2)
31 return a
TypeError: 'slice' object cannot be interpreted as an integer
报错,原因是
__getitem__()
传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断:
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self, new_name):
self.name = new_name
self.num = 0
print("%s 来了" % self.name)
def __del__(self):
print("%s 我去了" % self.name)
def __str__(self):
# 必须返回一个字符串
return "我是小猫[%s]" % self.name
def __call__(self):
print("执行__call__方法")
def __iter__(self):
return self
def __next__(self):
self.num += 1
if self.num > 6:
raise StopIteration()
print('%s 产下了第%d小猫'%(self.name,self.num))
def __getitem__(self, n):
if isinstance(n, int): # n是索引
a = "第1个小猫"
for x in range(n):
a = '第%d个小猫'% (x+2)
return a
elif isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a = "第1个小猫"
L = []
for x in range(stop):
a = '第%d个小猫'% (x+2)
L.append(a)
return L
tom = Cat("Tom")
print(tom[8])
print(tom[:2])
Tom 来了
Tom 我去了
第9个小猫
['第2个小猫', '第3个小猫']
__setitem__
和__delitem__
__setitem__()
方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()
方法,用于删除某个元素
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self, new_name):
self.name = new_name
self.num = 0
print("%s 来了" % self.name)
def __del__(self):
print("%s 我去了" % self.name)
def __str__(self):
# 必须返回一个字符串
return "我是小猫[%s]" % self.name
def __call__(self):
print("执行__call__方法")
def __iter__(self):
return self
def __next__(self):
self.num += 1
if self.num > 6:
raise StopIteration()
print('%s 产下了第%d小猫'%(self.name,self.num))
def __getitem__(self, n):
if isinstance(n, int): # n是索引
return self.__dict__[n]
elif isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
L = [self.__dict__[i] for i in range(start,stop)]
return L
def __setitem__(self, key, value):
self.__dict__[key] = value
print("添加", value, "成功")
def __delitem__(self, key):
if key in self.__dict__:
print("删除", self.__dict__[key], "成功")
self.__dict__.pop(key)
tom = Cat("Tom")
Tom 来了
for x in range(8):
tom[x+1] = '第%d个小猫'% (x+1)
添加 第1个小猫 成功
添加 第2个小猫 成功
添加 第3个小猫 成功
添加 第4个小猫 成功
添加 第5个小猫 成功
添加 第6个小猫 成功
添加 第7个小猫 成功
添加 第8个小猫 成功
for i in range(1,9):
print(tom[i])
第1个小猫
第2个小猫
第3个小猫
第4个小猫
第5个小猫
第6个小猫
第7个小猫
第8个小猫
print(tom[6:8])
['第6个小猫', '第7个小猫']
del tom[6]
print(tom[7])
print(tom[6])
删除 第6个小猫 成功
第7个小猫
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-410-20201facdff5> in <module>
1 del tom[6]
2 print(tom[7])
----> 3 print(tom[6])
<ipython-input-405-9dc9bf9cea6c> in __getitem__(self, n)
27 def __getitem__(self, n):
28 if isinstance(n, int): # n是索引
---> 29 return self.__dict__[n]
30 elif isinstance(n, slice): # n是切片
31 start = n.start
KeyError: 6
__getattr__
如果属性查找(attribute lookup)在实例以及对应的类中(通过__dict__
)失败, 那么会调用到类的__getattr__
函数, 如果没有定义这个函数,那么抛出AttributeError异常。由此可见,__getattr__
一定是作用于属性查找的最后一步,兜底
tom.sun
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-94-4ce2bf3c8da4> in <module>
----> 1 tom.sun
AttributeError: 'Cat' object has no attribute 'sun'
class Cat(dict):
"""描述类信息,这是一个喵喵"""
def __init__(self,name, *args, **kwargs):
super(Cat, self).__init__(*args, **kwargs)
self.name = name
self.num = 0
self.value = ''
def __del__(self):
pass
def __call__(self):
print("执行__call__方法")
def __iter__(self):
return self
def __next__(self):
self.num += 1
if self.num > 6:
raise StopIteration()
print('%s 产下了第%d小猫'%(self.name,self.num))
def __getitem__(self, n):
if isinstance(n, int): # n是索引
return self.__dict__[n]
elif isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
L = [self.__dict__[i] for i in range(start,stop)]
return L
def __setitem__(self, key, value):
self.__dict__[key] = value
print("添加", value, "成功")
def __delitem__(self, key):
if key in self.__dict__:
print("删除", self.__dict__[key], "成功")
self.__dict__.pop(key)
def __getattr__(self, name):
self.value = self.get(name,'sunchengquan')
if isinstance(self.value, dict):
self.value =Cat(self.name ,self.value)
return self.value
tom = Cat("Tom")
print(tom.sun)
sunchengquan
可以像访问属性一样访问dict中的键值对
tom = Cat("Tom",ab={'a': 1,'b':2}, c=True)
print(tom.ab)
print(tom.ab.b)
print(tom.c)
{'a': 1, 'b': 2}
2
True
__add__
; __sub__
; __mul__
;__truediv__
; __mod__
加运算,减运算,乘运算, 除运算,: 求余运算
class Cat(dict):
"""描述类信息,这是一个喵喵"""
def __init__(self,name, a, b, *args, **kwargs):
super(Cat, self).__init__(*args, **kwargs)
self.name = name
self.num = 0
self.value = ''
self.a = a
self.b = b
def __del__(self):
pass
def __call__(self):
print("执行__call__方法")
def __iter__(self):
return self
def __next__(self):
self.num += 1
if self.num > 6:
raise StopIteration()
print('%s 产下了第%d小猫'%(self.name,self.num))
def __getitem__(self, n):
if isinstance(n, int): # n是索引
return self.__dict__[n]
elif isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
L = [self.__dict__[i] for i in range(start,stop)]
return L
def __setitem__(self, key, value):
self.__dict__[key] = value
print("添加", value, "成功")
def __delitem__(self, key):
if key in self.__dict__:
print("删除", self.__dict__[key], "成功")
self.__dict__.pop(key)
def __getattr__(self, name):
self.value = self.get(name,'sunchengquan')
if isinstance(self.value, dict):
self.value =Cat(self.name ,self.a, self.b, self.value)
return self.value
def __add__(self,other):
return Cat(self.name, self.a + other.a, self.b + other.b)
def __sub__(self,other):
return Cat(self.name, self.a - other.a, self.b - other.b)
def __mul__(self,other):
return Cat(self.name, self.a * other.a, self.b * other.b)
def __truediv__(self,other):
return Cat(self.name, self.a / other.a, self.b / other.b)
def __mod__(self,other):
return Cat(self.name, self.a % other.a, self.b % other.b)
def __str__(self):
return '(%d, %d)' % (self.a, self.b)
tom1 = Cat("Tom",8, 4)
tom2 = Cat("Tom",3, 4)
print(tom1+tom2)
print(tom1-tom2)
print(tom1*tom2)
print(tom1/tom2)
print(tom1%tom2)
(11, 8)
(5, 0)
(24, 16)
(2, 1)
(2, 0)
__cmp__
对 int、str 等内置数据类型排序时,Python的 sorted()
按照默认的比较函数 cmp 排序,但是,如果对一组 Student 类的实例排序时,就必须提供我们自己的特殊方法 __cmp__()
:
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self,name, a, b, *args, **kwargs):
super(Cat, self).__init__(*args, **kwargs)
self.name = name
self.num = 0
self.value = ''
self.a = a
self.b = b
def __del__(self):
pass
def __call__(self):
print("执行__call__方法")
def __iter__(self):
return self
def __next__(self):
self.num += 1
if self.num > 6:
raise StopIteration()
print('%s 产下了第%d小猫'%(self.name,self.num))
def __add__(self,other):
return Cat(self.name, self.a + other.a, self.b + other.b)
def __sub__(self,other):
return Cat(self.name, self.a - other.a, self.b - other.b)
def __mul__(self,other):
return Cat(self.name, self.a * other.a, self.b * other.b)
def __truediv__(self,other):
return Cat(self.name, self.a / other.a, self.b / other.b)
def __mod__(self,other):
return Cat(self.name, self.a % other.a, self.b % other.b)
def __cmp__(self, s):
if(self.a<s.a):
return 1
if(self.a>s.a):
return -1
if(self.a==s.a):
if(self.name>s.name):
return 1;
if(self.name<s.name):
return -1
return 0
def __str__(self):
return '(%d, %d)' % (self.a, self.b)
L = [Cat('Tim', 99,1), Cat('Bob', 88,1), Cat('Alice', 99,1),100 ,"sun"]
print(sorted(L))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-303-29f737bd28d8> in <module>
1 L = [Cat('Tim', 99,1), Cat('Bob', 88,1), Cat('Alice', 99,1),100 ,"sun"]
----> 2 print(sorted(L))
TypeError: '<' not supported between instances of 'Cat' and 'Cat'
__lt__
; __le__
;__gt__
; __ge__
;__eq__
;__ne__
上面没有解决的问题:
TypeError: '<' not supported between instances of 'Cat' and 'Cat'
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self,name, a, b, *args, **kwargs):
self.name = name
self.num = 0
self.value = ''
self.a = a
self.b = b
def __del__(self):
pass
def __call__(self):
print("执行__call__方法")
def __iter__(self):
return self
def __next__(self):
self.num += 1
if self.num > 6:
raise StopIteration()
print('%s 产下了第%d小猫'%(self.name,self.num))
def __add__(self,other):
return Cat(self.name, self.a + other.a, self.b + other.b)
def __sub__(self,other):
return Cat(self.name, self.a - other.a, self.b - other.b)
def __mul__(self,other):
return Cat(self.name, self.a * other.a, self.b * other.b)
def __truediv__(self,other):
return Cat(self.name, self.a / other.a, self.b / other.b)
def __mod__(self,other):
return Cat(self.name, self.a % other.a, self.b % other.b)
def __lt__(self,other):
if self.a < other.a:
return True
else:
return False
def __le__(self,other):
if self.a <= other.a:
return True
else:
return False
def __ge__(self,other):
if self.a >= other.a:
return True
else:
return False
def __gt__(self,other):
if self.a > other.a:
return True
else:
return False
def __eq__(self,other):
if self.a == other.a:
return True
else:
return False
def __ne__(self,other):
if self.a != other.a:
return True
else:
return False
def __cmp__(self, other):
if(self.a < other.a):
return 1
if(self.a > other.a):
return -1
if(self.a == other .a):
if(self.name > other.name):
return 1;
if(self.name < other.name):
return -1
return 0
def __str__(self):
return '(%d, %d)' % (self.a, self.b)
c1 = Cat('Tim', 97, 1)
c2 = Cat('Bob', 88, 1)
c3 = Cat('Alice', 99, 1)
print(c1 < c2)
print(c1 <= c2)
print(c1 == c2)
print(c1 != c2)
False
False
False
True
l = [c1,c2,c3]
print(sorted(l))
[<__main__.Cat object at 0x7f3cbc124f28>, <__main__.Cat object at 0x7f3cbc124208>, <__main__.Cat object at 0x7f3cbc124128>]
__repr__
上面没有解决的问题
[<__main__.Cat object at 0x7f3cbc124f28>, <__main__.Cat object at 0x7f3cbc124208>, <__main__.Cat object at 0x7f3cbc124128>]
__repr__
和__str__
这两个方法都是用于显示的,__str__是面向用户的,而__repr__面向程序员
重构__repr__
方法后,不管直接输出对象还是通过print打印的信息都按我们__repr__方法中定义的格式进行显示了
重构__str__
你会发现,直接输出对象ts时并没有按我们__str__方法中定义的格式进行输出,而用print输出的信息却改变了
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self,name, a, b, *args, **kwargs):
self.name = name
self.num = 0
self.value = ''
self.a = a
self.b = b
def __del__(self):
pass
def __call__(self):
print("执行__call__方法")
def __iter__(self):
return self
def __next__(self):
self.num += 1
if self.num > 6:
raise StopIteration()
print('%s 产下了第%d小猫'%(self.name,self.num))
def __add__(self,other):
return Cat(self.name, self.a + other.a, self.b + other.b)
def __sub__(self,other):
return Cat(self.name, self.a - other.a, self.b - other.b)
def __mul__(self,other):
return Cat(self.name, self.a * other.a, self.b * other.b)
def __truediv__(self,other):
return Cat(self.name, self.a / other.a, self.b / other.b)
def __mod__(self,other):
return Cat(self.name, self.a % other.a, self.b % other.b)
def __lt__(self,other):
if self.a < other.a:
return True
else:
return False
def __le__(self,other):
if self.a <= other.a:
return True
else:
return False
def __ge__(self,other):
if self.a >= other.a:
return True
else:
return False
def __gt__(self,other):
if self.a > other.a:
return True
else:
return False
def __eq__(self,other):
if self.a == other.a:
return True
else:
return False
def __ne__(self,other):
if self.a != other.a:
return True
else:
return False
def __cmp__(self, other):
if self.a < other.a:
return 1
if self.a > other.a:
return -1
if self.a == other.a:
return 0
def __str__(self):
return '(%s %d, %d)' % (self.name, self.a, self.b)
__repr__ = __str__
l = [Cat('Tim', 97, 1), Cat('Bob', 88, 1), Cat('Mary', 99, 1 ), Cat('Alice', 99, 2)]
print(sorted(l))
[(Bob 88, 1), (Tim 97, 1), (Mary 99, 1), (Alice 99, 2)]
__bool__
对类进行布尔值判断
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self,name, a, b, *args, **kwargs):
self.name = name
self.a = a
self.b = b
def __le__(self,other):
if self.a <= other.a:
return True
else:
return False
def __ge__(self,other):
if self.a >= other.a:
return True
else:
return False
def __gt__(self,other):
if self.a > other.a:
return True
else:
return False
def __eq__(self,other):
if self.a == other.a:
return True
else:
return False
def __ne__(self,other):
if self.a != other.a:
return True
else:
return False
def __str__(self):
return '(%s %d, %d)' % (self.name, self.a, self.b)
__repr__ = __str__
def __bool__(self):
return bool(self.a > 5)
tom1 = Cat("Tom",1,2)
tom2 = Cat("Tom",8,2)
print(tom1)
print(tom2)
if tom1:
print("tom1")
if tom2:
print("tom2")
(Tom 1, 2)
(Tom 8, 2)
tom2
@staticmethod
静态方法
只是名义上归类管理, 实际上在静态方法里访问不了类或实例中的任何属性
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self,name, *args, **kwargs):
self.name = name
@staticmethod #实际上跟类没什么关系了
def eat(self):
print("%s is eating %s" %(self.name,'dd'))
def talk(self):
print("%s is talking"% self.name)
tom = Cat("Tom")
tom.eat()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-344-688b08415254> in <module>
12
13 tom = Cat("Tom")
---> 14 tom.eat()
TypeError: eat() missing 1 required positional argument: 'self'
怎么解决上面的问题?
调用时主动传递实例本身给eat方法,即tom.eat(tom)
tom.eat(tom)
Tom is eating dd
在eat方法中去掉self参数,但这也意味着,在eat中不能通过self.调用实例中的其它变量了
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self,name, *args, **kwargs):
self.name = name
@staticmethod #实际上跟类没什么关系了
def eat():
print("%s is eating %s" %(self.name,'dd'))
def talk(self):
print("%s is talking"% self.name)
tom = Cat("Tom")
tom.eat()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-347-116f9837bbbc> in <module>
11 print("%s is talking"% self.name)
12 tom = Cat("Tom")
---> 13 tom.eat()
<ipython-input-347-116f9837bbbc> in eat()
6 @staticmethod #实际上跟类没什么关系了
7 def eat():
----> 8 print("%s is eating %s" %(self.name,'dd'))
9
10 def talk(self):
NameError: name 'self' is not defined
class Cat(object):
"""描述类信息,这是一个喵喵"""
def __init__(self,name, *args, **kwargs):
self.name = name
@staticmethod #实际上跟类没什么关系了
def eat():
print("eating %s" %('dd'))
def talk(self):
print("%s is talking"% self.name)
tom = Cat("Tom")
tom.eat()
eating dd
@classmethod
类方法:只能访问类变量,不能访问实例变量
class Cat(object):
name = "huazai"
def __init__(self,name):
self.name = name
@classmethod
def eat(self):
print("%s is eating %s" %(self.name,'dd'))
def talk(self):
print("%s is talking"% self.name)
cat = Cat("Tom")
cat.eat()
huazai is eating dd
class Cat(object):
name = "huazai"
def __init__(self,name):
self.name = name
@staticmethod
def show_info():
print("帮助信息:\n\t这是一个大肥喵\n\t主人是xxxxxxxx")
@classmethod
def eat(cls):
print("%s is eating %s" %(cls.name,'dd'))
def talk(self):
print("%s is talking"% self.name)
cat = Cat("Tom")
cat.eat()
cat.show_info()
huazai is eating dd
帮助信息:
这是一个大肥喵
主人是xxxxxxxx
@property
属性方法
把一个方法变成一个静态属性
调用会出以下错误, 说NoneType is not callable, 因为eat此时已经变成一个静态属性了, 不是方法了, 想调用已经不需要加()号了,直接cat.eat就可以了
class Cat(object):
name = "huazai"
def __init__(self,name):
self.name = name
@property
def eat(self):
print("%s is eating %s" %(self.name,'dd'))
def talk(self):
print("%s is talking"% self.name)
cat = Cat("Tom")
cat.eat()
Tom is eating dd
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-352-254f28ff15c1> in <module>
13
14 cat = Cat("Tom")
---> 15 cat.eat()
TypeError: 'NoneType' object is not callable
cat = Cat("Tom")
cat.eat
Tom is eating dd
修改属性值
class Cat(object):
'''这个类是描述狗这个对象的'''
def __init__(self,name):
self.name = name
self.__food = None
@property
def eat(self):
print("%s is eating %s" %(self.name,self.__food))
@eat.setter
def eat(self,food):
print("set to food:",food)
self.__food = food
@eat.deleter
def eat(self):
del self.__food
print("删完了")
def talk(self):
print("%s is talking"% self.name)
cat = Cat("Tom")
cat.eat
Tom is eating None
cat.eat = 'fish'
set to food: fish
cat.eat
Tom is eating fish
案例演示
把一个方法变成静态属性有什么卵用呢?既然想要静态变量,那直接定义成一个静态变量不就得了么?well, 以后你会需到很多场景是不能简单通过 定义 静态属性来实现的, 比如 ,你想知道一个航班当前的状态,是到达了、延迟了、取消了、还是已经飞走了, 想知道这种状态你必须经历以下几步:
- 连接航空公司API查询
- 对查询结果进行解析
- 返回结果给你的用户
因此这个status属性的值是一系列动作后才得到的结果,所以你每次调用时,其实它都要经过一系列的动作才返回你结果,但这些动作过程不需要用户关心, 用户只需要调用这个属性就可以
class Flight(object):
def __init__(self,name):
self.flight_name = name
def checking_status(self):
print("checking flight %s status " % self.flight_name)
return 0
@property
def flight_status(self):
status = self.checking_status() #相当于调航空公司的API
if status == 0 :
print("flight got canceled...")
elif status == 1 :
print("flight is arrived...")
elif status == 2:
print("flight has departured already...")
else:
print("cannot confirm the flight status...,please check later")
f = Flight("CA980")
f.flight_status
checking flight CA980 status
flight got canceled...
这个flight_status已经是个属性了, 那我能否给它赋值呢?
需要通过@proerty.setter装饰器再装饰一下,此时 你需要写一个新方法, 对这个flight_status进行更改
class Flight(object):
def __init__(self,name):
self.flight_name = name
self.status = self.checking_status() #相当于调航空公司的API
def checking_status(self):
print("checking flight %s status " % self.flight_name)
return 0
@property
def flight_status(self):
if self.status == 0 :
print("flight got canceled...")
elif self.status == 1 :
print("flight is arrived...")
elif self.status == 2:
print("flight has departured already...")
else:
print("cannot confirm the flight status...,please check later")
@flight_status.setter
def flight_status(self,status):
status_dic = {
0: "canceled",
1: "arrived",
2: "departured"
}
self.status = status
print("\033[31;1mHas changed the flight status to \033[0m", status_dic.get(status))
@flight_status.deleter
def flight_status(self):
print("status got removed...")
self.status = None
f = Flight("CA980")
f.flight_status
checking flight CA980 status
flight got canceled...
f.flight_status = 2 #触发@flight_status.setter
f.flight_status
[31;1mHas changed the flight status to [0m departured
flight has departured already...
del f.flight_status #触发@flight_status.deleter
status got removed...
f.flight_status
cannot confirm the flight status...,please check later
__new__
__new__
是一个 由 object
基类提供的 内置的静态方法,主要作用有两个:
* 1) 在内存中为对象 分配空间
* 2) 返回 对象的引用
Python
的解释器获得对象的 引用 后,将引用作为 第一个参数,传递给__init__
方法
重写
__new__
方法 的代码非常固定!
- 重写
__new__
方法 一定要return super().__new__(cls)
- 否则 Python 的解释器 得不到 分配了空间的 对象引用,就不会调用对象的初始化方法
- 注意:
__new__
是一个静态方法,在调用时需要 主动传递cls
参数
class Cat(object):
def __new__(cls, *args, **kwargs):
print("创建对象,分配空间")
instance = super().__new__(cls)
return instance
def __init__(self):
print("初始化")
cat = Cat()
创建对象,分配空间
初始化
cat1 = Cat()
print(cat1)
cat2 = Cat()
print(cat2)
print('-'*50)
class Cat(object):
pass
cat1 = Cat()
print(cat1)
cat2 = Cat()
print(cat2)
创建对象,分配空间
初始化
<__main__.Cat object at 0x7f3cbc0dff60>
创建对象,分配空间
初始化
<__main__.Cat object at 0x7f3cbc0dfcf8>
--------------------------------------------------
<__main__.Cat object at 0x7f3cbc0dff98>
<__main__.Cat object at 0x7f3cbc0dff60>
单例设计模式
- 单例设计模式
- 目的 —— 让 类 创建的对象,在系统中 只有 唯一的一个实例
- 每一次执行
类名()
返回的对象,内存地址是相同的
单例设计模式的应用场景
- 音乐播放 对象
- 回收站 对象
- 打印机 对象
单例流程
1. 定义一个 **类属性**,初始值是 `None`,用于记录 **单例对象的引用**
2. 重写 `__new__` 方法
3. 如果 **类属性** `is None`,调用父类方法分配空间,并在类属性中记录结果
4. 返回 **类属性** 中记录的 **对象引用**
class Cat(object):
# 定义类属性记录单例对象引用
instance = None
def __new__(cls, *args, **kwargs):
# 1. 判断类属性是否已经被赋值
if cls.instance is None:
cls.instance = super().__new__(cls)
print("创建对象,分配空间")
# 2. 返回类属性的单例引用
return cls.instance
def __init__(self):
print("初始化")
cat1 = Cat()
print(cat1)
cat2 = Cat()
print(cat2)
创建对象,分配空间
初始化
<__main__.Cat object at 0x7f3cbc0c44e0>
初始化
<__main__.Cat object at 0x7f3cbc0c44e0>
只执行一次初始化工作
- 在每次使用
类名()
创建对象时,Python
的解释器都会自动调用两个方法:__new__
分配空间__init__
对象初始化
- 在上一小节对
__new__
方法改造之后,每次都会得到 第一次被创建对象的引用 - 但是:初始化方法还会被再次调用
需求
- 让 初始化动作 只被 执行一次
解决办法
- 定义一个类属性
init_flag
标记是否 执行过初始化动作,初始值为False
- 在
__init__
方法中,判断init_flag
,如果为False
就执行初始化动作 - 然后将
init_flag
设置为True
- 这样,再次 自动 调用
__init__
方法时,初始化动作就不会被再次执行 了
class Cat(object):
instance = None
init_flag = False
def __new__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance = super().__new__(cls)
print("创建对象,分配空间")
# 2. 返回类属性的单例引用
return cls.instance
def __init__(self):
if not Cat.init_flag:
print("初始化")
Cat.init_flag = True
cat1 = Cat()
print(cat1)
cat2 = Cat()
print(cat2)
创建对象,分配空间
初始化
<__main__.Cat object at 0x7f3cbc0c4be0>
<__main__.Cat object at 0x7f3cbc0c4be0>
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)
有了ListMetaclass,我们在定义类的时候还要指示使用ListMetaclass来定制类,传入关键字参数metaclass
:
class MyList(list, metaclass=ListMetaclass):
pass
当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList
时,要通过ListMetaclass.__new__()
来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。
__new__()
方法接收到的参数依次是:
当前准备创建的类的对象;
类的名字;
类继承的父类集合;
类的方法集合。
测试一下MyList是否可以调用add()方法:
L = MyList()
L.add(1)
L.add(1)
print(L)
[1, 1]
而普通的list
没有add()
方法:
L2 = list()
L2.add(1)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-12-e9551a7fbae7> in <module>
1 L2 = list()
----> 2 L2.add(1)
AttributeError: 'list' object has no attribute 'add'
动态修改有什么意义?直接在MyList定义中写上add()方法不是更简单吗?正常情况下,确实应该直接写,通过metaclass修改纯属变态。
class MyList1(list):
def add(self, value):
self.append(value)
L = MyList1()
L.add(1)
L.add(1)
print(L)
[1, 1]
但是,总会遇到需要通过metaclass修改类定义的。ORM就是一个典型的例子。
ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。
要编写一个ORM框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来。
让我们来尝试编写一个ORM框架。
编写底层模块的第一步,就是先把调用接口写出来。比如,使用者如果使用这个ORM框架,想定义一个User
类来操作对应的数据库表User
,我们期待他写出这样的代码:
class User(Model):
# 定义类的属性到列的映射:
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
# 创建一个实例:
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
# 保存到数据库:
u.save()
其中,父类Model
和属性类型StringField、IntegerField
是由ORM框架提供的,剩下的魔术方法比如save()
全部由metaclass自动完成。虽然metaclass的编写会比较复杂,但ORM的使用者用起来却异常简单。
现在,我们就按上面的接口来实现该ORM。
首先来定义Field
类,它负责保存数据库表的字段名和字段类型:
class Field(object):
def __init__(self, name, column_type):
self.name = name
self.column_type = column_type
def __str__(self):
return '<%s:%s>' % (self.__class__.__name__, self.name)
在Field
的基础上,进一步定义各种类型的Field
,比如StringField,IntegerField
等等:
class StringField(Field):
def __init__(self, name):
super(StringField, self).__init__(name, 'varchar(100)')
class IntegerField(Field):
def __init__(self, name):
super(IntegerField, self).__init__(name, 'bigint')
下一步,就是编写最复杂的ModelMetaclass
了:
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
if name=='Model':
return type.__new__(cls, name, bases, attrs)
print('Found model: %s' % name)
mappings = dict()
for k, v in attrs.items():
if isinstance(v, Field):
print('Found mapping: %s ==> %s' % (k, v))
mappings[k] = v
for k in mappings.keys():
attrs.pop(k)
attrs['__mappings__'] = mappings # 保存属性和列的映射关系
attrs['__table__'] = name # 假设表名和类名一致
return type.__new__(cls, name, bases, attrs)
以及基类Model:
class Model(dict, metaclass=ModelMetaclass):
def __init__(self, **kw):
super(Model, self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
def save(self):
fields = []
params = []
args = []
for k, v in self.__mappings__.items():
fields.append(v.name)
params.append('?')
args.append(getattr(self, k, None))
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
print('SQL: %s' % sql)
print('ARGS: %s' % str(args))
当用户定义一个class User(Model)
时,Python解释器首先在当前类User
的定义中查找metaclass
,如果没有找到,就继续在父类Model
中查找metaclass
,找到了,就使用Model中定义的metaclass
的ModelMetaclass
来创建User
类,也就是说,metaclass
可以隐式地继承到子类,但子类自己却感觉不到。
在ModelMetaclass
中,一共做了几件事情:
1. 排除掉对Model
类的修改;
2. 在当前类(比如User)中查找定义的类的所有属性,如果找到一个Field
属性,就把它保存到一个__mappings__
的dict中,同时从类属性中删除该Field
属性,否则,容易造成运行时错误(实例的属性会遮盖类的同名属性);
3. 把表名保存到__table__
中,这里简化为表名默认为类名。
在Model
类中,就可以定义各种操作数据库的方法,比如save(),delete(),find(),update
等等。
我们实现了save()
方法,把一个实例保存到数据库中。因为有表名,属性到字段的映射和属性值的集合,就可以构造出INSERT语句。
编写代码试试:
class User(Model):
# 定义类的属性到列的映射:
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
# 创建一个实例:
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
# 保存到数据库:
u.save()
Found model: User
Found mapping: id ==> <IntegerField:id>
Found mapping: name ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
SQL: insert into User (id,username,email,password) values (?,?,?,?)
ARGS: [12345, 'Michael', 'test@orm.org', 'my-pwd']