面向过程和面向对象的区别:
类的作用:
类的组成:
根据类创建对象的底层原理:
左边的Money和one都是变量名
属性和变量的区别:
python查找机制:
删除类(不是对象)的属性:
包括修改类的属性也是不能通过对象进行修改,相当于类是类的属性,对象是对象的属性
class Money:
age = 15
one = Money()
del one.age # 报错,因为one是对象,不能直接删除类的属性的
one.age += 5 # one.age = one.age + 5(右边one.age显示Money中的属性age=15,加完后新增one对象的属性age)
print(one.age) # 15
print(Money.age) # 10
__dict__对于类和对象的区别:
class Money:
age = 15
one = Money()
one.__dict__["name"] = 'psj'
print(one.__dict__) # 不会报错,对象可以直接修改__dict__
Money.__dict__['name'] = 'psw'
print(Money.__dict__) # 报错,类不能直接对__dict__进行修改
限制对象属性添加:
class Person:
__slots__ = ["age", "sex"] # 列表形式
pass
p1 = Person()
p1.age = 20
p1.sex = "male"
p1.height = 180 # 此处注意上面没写这个height,结果会报错显示没有属性height
方法的划分:
class Money:
# 第一个参数self是一个实例
def shilifangfa(self):
print("这是一个实例方法", self)
# 第一个参数cls是一个类
@classmethod
def leifangfa(cls):
print("这是一个类方法", cls)
@staticmethod
def jingtaifangfa():
print("这是一个静态方法")
m = Money()
# 下面两个输出值一样
m.shilifangfa() # 这是一个实例方法 <__main__.Money object at 0x0000027FBFB94C70>
print(m) # <__main__.Money object at 0x0000027FBFB94C70>
Money.leifangfa() # 这是一个类方法 <class '__main__.Money'>
Money.jingtaifangfa() # 这是一个静态方法
print(m.__dict__) # {} 对应了图中注意中的第二点,方法都是存储在类中的,没有存储在实例中的
print(Money.__dict__) # {'__module__': '__main__', 'shilifangfa': <function Money.shilifangfa at 0x0000020986D8F280>, 'leifangfa': <classmethod object at 0x0000020986FF0CD0>, 'jingtaifangfa': <staticmethod object at 0x0000020988CE4A90>, '__dict__': <attribute '__dict__' of 'Money' objects>, '__weakref__': <attribute '__weakref__' of 'Money' objects>, '__doc__': None}
实例方法:
class Money:
def shilifangfa(self):
print("这是一个实例方法", self)
# 报错,这是静态方法的形式,需要加上@staticmethod
def fangfa():
print("test")
tips:根据场景不同选择定义实例方法还是类方法,比如:
l = [1,2,3]
l.sort(就两个参数,不需要list) # 使用实例方法
list.sort(list对象,后面还要两个参数)
类方法:
class Money:
@classmethod
def leifangfa(cls):
print("这是一个类方法", cls)
Money.leifangfa() # 这是一个类方法 <class '__main__.Money'>
# 另一种调用方式
p = Money.leifangfa
p() # 这是一个类方法 <class '__main__.Money'>
# The instance is ignored except for its class(实例被类给忽略了)
a = Money()
a.leifangfa() # 这是一个类方法 <class '__main__.Money'>
不同类型的方法访问不同的属性:
class Person:
age = 0
def shilifangfa(self):
print(self)
print(self.age)
print(self.num)
@classmethod
def leifangfa(cls):
print("这是一个类方法", cls)
print(cls.age)
print(cls.num)
@staticmethod
def jingtaifangfa():
print("这是一个静态方法")
p = Person()
p.num = 10
p.shilifangfa() # 不会报错
p.leifangfa() # 报错 type object 'Person' has no attribute 'num'
Person.leifangfa() # 同样报错 type object 'Person' has no attribute 'num'
tips:可以通过对象属性去找到类属性,但是不能通过类属性找到对象属性
元类:
class Person:
pass
print(Person.__class__) # type
print(int.__class__) # type
类对象的创建方式:
# 自动在内存中创建类对象
class Person:
num = 10
def run(self):
pass
p = Person()
# 手动调用type创建类对象
def run():
pass
# 第一个参数是类名(左边的Person是引用对象的名字,相当于class Person的Person),第二个参数是基类,第三个是属性和方法
Person = type('Person', (), {'num': 10, 'run': run})
p1 = Person()
print(p1.__dict__) # {}
print(Person.__dict__) # {'num': 10, 'run': <function run at 0x000001B7D4A363A0>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
类对象的创建(元类的查找机制):
__metaclass__ = xxx # 模块的(没有就找内置的type)
def Animal:
__metaclass__ = xxx # 父类的(没有就找模块的)
pass
def Dog(Animal):
__metaclass__ = xxx # 类中的(没有就找父类的)
pass
受保护的属性:
class Animal:
_x = 10
def run(self):
print(Animal._x)
print(self._x)
class Dog(Animal):
def run(self):
print(Dog._x)
print(self._x)
# 测试类内部是否正常访问(可以)
a = Animal()
a.run()
# 测试子类内部是否正常访问(可以)
b = Dog()
b.run()
# 测试模块内是否正常访问(可以)
print(Animal._x)
print(Dog._x)
print(a._x)
print(b._x)
# 测试跨模块是否正常访问
_a = 111
__all__ = ['_a']
在另一个py文件中(上面的文件名为test.py):
from test import *
print(_a) # 不在test.py中加入__all__ = ['_a']就报错
import test
print(test._a) # 没问题,但是有警告
私有属性:
class Animal:
__x = 10
def run(self):
print(Animal.__x)
print(self.__x)
class Dog(Animal):
def run(self):
print(Dog.__x)
print(self.__x)
# 测试类内部是否正常访问(可以)
a = Animal()
a.run()
# 测试子类内部是否正常访问(不行)
b = Dog()
b.run()
# 测试模块内是否正常访问(不行)
print(Animal.__x)
print(Dog.__x)
print(a.__x)
print(b.__x)
# 测试跨模块是否正常访问
__all__ = ['_a']
_a = 111 # 与受保护的属性一样
私有(伪私有)属性的实现机制:
class Animal:
__x = 10
pass
print(Animal.__dict__) #{'__module__': '__main__', '_Animal__x': 10, ...}
print(Animal._Animal__x) # 可以输出
私有属性的应用场景:
class Animal:
def __init__(self):
self.__age = 10
def setAge(self, value):
self.__age = value
def getAge(self):
return self.__age
a = Animal()
print(a.__age) # 访问不了,只能通过_Animal__age访问
print(a.getAge()) # 可以得到数值
变量添加下划线:
class_ #与类中的关键字进行区分
__x__ # 大部分为系统内置,尽量避免
只读属性:
# 方式1:通过双下划线进行全部隐藏,然后通过get方法部分可读
class Animal:
def __init__(self):
self.__age = 10
def getAge(self):
return self.__age
a = Animal()
print(a.__age) # 报错
a.__age = 10
print(a.__age) # 这个__age是实例属性,不是类属性了
print(a.getAge()) #成功输出
# 方式2:使用property,它可以以使用属性的方式使用该方法
class Animal:
def __init__(self):
self.__age = 10
@property
def getAge(self):
return self.__age
a = Animal()
print(a.getAge) # 成功输出
a.getAge = 666 # 报错,无法进行修改(下面使用property可以解决)
经典类和新式类:
property在新式类中的使用:
# 第一种使用方式
class Animal:
def __init__(self):
self.__age = 10
def get_age(self):
return self.__age
def set_age(self, value):
self.__age = value
age = property(get_age, set_age)
a = Animal()
print(a.age)
a.age = 100 # 不是新增一个实例属性,而是在类属性上进行修改
print(a.age)
print(a.__dict__) # {'_Animal__age': 100}
# 第二种使用方式
class Animal:
def __init__(self):
self.__age = 10
@property
def age(self):
return self.__age
@age.setter # 这个age对应上面的函数名字
def age(self, value): # 这里函数名也为age是为规范
self.__age = value
a = Animal()
print(a.age)
a.age = 100
print(a.age)
print(a.__dict__)
tips:property在经典类中只能关联读取方法,其他方法不能关联
常用内置属性:
私有方法:
class Animal:
def __run(self):
print('xxx')
a = Animal()
a.__run # 和私有属性一样报错
# 直接定义转换后的方法,会覆盖之前的方法
class Animal:
def __run(self):
print('xxx')
def _Animal__run(self):
print('yyy')
a = Animal()
a._Animal__run() # 打印yyy
print(Animal.__dict__)
内置特殊方法:
# 1.__str__方法
class Person:
def __init__(self, age):
self.age = age
def __str__(self):
return '年龄:%s'%str(self.age)
def __repr__(self):
return 'repr'
p = Person(18)
print(p) # 年龄:18
# 2.__repr__方法:面向开发人员
print(repr(p)) # 当类没有实现__repr__函数时,会返回<__main__.Person object at 0x0000029C363A0DC0>
print(repr(p)) # 当类实现__repr__函数后会返回实现的函数的内容:'repr';当类没有实现__str__时,print(p)会调用__repr__
# 3.__call__方法:使得实例对象也可以像方法一样被调用
class Person:
def __call__(self, *args, **kwargs):
print(args,kwargs)
p = Person()
p(12,23,age=22) # 不实现__call__方法直接执行会报错;(12, 23) {'age': 22}(前面两个参数被传入到args,后面带名称的参数传入kwargs形成字典)
# 场景实例
class PenFactory:
def __init__(self, g_type):
self.g_type = g_type
def __call__(self, animal):
print('使用%s画%s'%(self.g_type, animal))
p = PenFactory('钢笔')
p('狗') # 无需传入多个重复的参数'钢笔'
p('猫')
# 4.索引操作:可以以索引的形式操作对象,方法有__setitem__,__getitem__,__delitem__
class Person:
def __init__(self):
self.cache = {}
def __setitem__(self, key, value):
self.cache[key] = value
def __getitem__(self, item):
return self.cache[item]
def __delitem__(self, key):
del self.cache[key]
p = Person()
p['age'] = 10 # 如果没有索引操作__setitem__,这一步会报错
print(p['age']) # 10
print(p.__dict__) # {'cache': {'age': 10}}
# 5.比较操作:自定义方法去比较实例对象的大小,有__eq__,__lt__等等
class Person:
def __init__(self, age, height):
self.age = age
self.height = height
def __eq__(self, other):
return self.age == other.age
def __lt__(self, other):
pass
p1 = Person(22, 180)
p2 = Person(22, 180)
print(p1 == p2) # True.如果没有__eq__方法,则python3会报错,python2会比较两个对象的内存地址大小
print(p1 <= p2) # 要判断小于等于,需要去分别实现__lt__和__eq__方法(或直接实现__le__方法)
# 也可以使用functools装饰器自动补全所有比较方法,但是前提要实现<,>,<=,>=至少其中一种方法
import functools
@functools.total_ordering
class Person:
def __init__(self, age, height):
self.age = age
self.height = height
def __lt__(self, other):
# 上下文bool值:__bool__方法
class Person:
def __init__(self, age):
self.age = age
def __bool__(self):
return self.age >= 18
p = Person(20)
if p: # 如果没有实现__bool__方法,则if p恒为真
print('成年')
# 6.遍历操作:
# 方法1:__getitem__方法
class Person:
def __init__(self):
self.result = 1
def __getitem__(self, item): # 将返回的值给下面代码的i
self.result += 1
if self.result >= 10:
raise StopIteration('停止遍历')
return self.result
pass
p = Person()
for i in p:
print(i) # 如果没有实现__getitem__方法,则对象是无法进行遍历的(即不属于迭代器,也不是可迭代对象)
# 方法2:__iter__方法和__next__方法
class Person:
def __iter__(self): # 1.将self变成一个迭代器
return self
def __next__(self): # 2.一个迭代器需要有next方法(有next方法不一定是迭代器)
return '传给i值'
p = Person()
for i in p: # 3.__next__方法返回的值传给了i进行输出
print(i)
import collections
print(isinstance(p,collections.Iterator)) # True,说明对象p已经是一个迭代器了(迭代器需要实现两个方法)
print(isinstance(p,collections.Iterable)) # True,但是只能说明p是一个可迭代对象(迭代对象只需要实现next方法)
描述器(data descriptors):
作用:接管一个类的增删改查操作;描述器是一个对象,这个对象里面有set,get,del方法;外界对类的属性进行操作时,会转而通过描述器对属性进行操作
方法1:参考’property在新式类中的使用’
方法2:创建一个描述器对象
# Age就是一个描述器
class Age:
def __set__(self, instance, value):
print('set')
def __get__(self, instance, owner):
print('get')
def __del__(self):
print('del')
class Person:
age = Age()
p = Person()
p.age = 10
如果知道p.age调用的是描述器而不是类的age属性:
资料描述器(实现了get和set)>实例属性>非资料描述器(只实现了get)
# 此时为资料描述器
class Age:
def __set__(self, instance, value):
print('set')
def __get__(self, instance, owner):
print('get')
def __del__(self):
print('del')
class Person:
def __init__(self):
self.age = 10
age = Age()
p = Person()
p.age = 10
print(p.age)
# 打印set set get None del.打印了两次set是因为此时的age是描述器不是属性age,所以在init方法中self.age=10执行一次描述器的set方法
tips:如果创建多个Person实例,描述器age对于每个实例是共享的
参考视频:https://www.bilibili.com/video/BV1A4411v7b2 (p1-p82)