Python中的数据类型都属于类。int、str、list都是Python定义好的数据类型类。
print(type(list))#<class 'type'> print(type(list()))#<class 'list'>
一、自定义数据类型
一、语法
class 类名():pass #类名 要求首字母大写 #()可写可省略。 #pass在这里只是用来保证语法不报错,本来应该是类体
二、示例
class Person():passclass Cat():pass
三、创建对象的语法格式
对象名=类名() #对象名是一个变量名,数据类型是 该类的类型 #这个括号一定要加,不加的话相当于给类名起了一个临时别名
示例:
class Person:pass p=Person() y=Person print(type(p),type(y),type(y())) #<class '__main__.Person'> <class 'type'> <class '__main__.Person'> #p是Person类型的对象,y是type类型的对象,类名就是一个type对象
二、类的组成
__init__(self) 定义 实例方法,指定初始化类的参数(默认构造函数)
①类中的函数称为方法。
②实例属性和实例方法指的是根据不同的实例变量值和方法的结果是不同的。类属性是类固有的,每个实例都共用这一个属性。实例属性必须是在__init__中定义的。并且,实例属性在该初始化方法中定义之后,可以在整个类中用self.来使用。
③初始化方法指定了一个类在定义对象时需要传递的参数,self参数是对象自带的,不需要传入。如果不用指定实例属性,也可以没有初始化方法。
一、类属性和实例属性
类属性,使用类名打点调用,包括类内部也是如此。
class Student:#类属性:定义在类中的变量,方法外的变量school='Jlu'#初始化方法def __init__(self,name,age):#name和age是方法的参数,可以忽略,局部变量self.name=name # self打点的 self.name是一个实例属性,name是局部变量,将局部变量的值赋值给实例属性self.age=age #实例属性的名称和局部变量的名称可以相同
实例属性,使用对象名打点调用
二、实例方法
class Student:#类属性:定义在类中的变量school='Jlu'#初始化方法def __init__(self,name,age):self.name=nameself.age=age#实例方法def show(self):print(f'我叫:{self.name},今年{self.age}岁了,来自{Student.school}')
三、静态方法
静态方法是类有的,而不是实例有的,不能使用实例属性,也不能调用实例方法。
静态方法是可以直接被类内函数调用的。
class Student:school='Jlu'def __init__(self,name,age):self.name=nameself.age=agedef show(self):print(f'我叫:{self.name},今年{self.age}岁了')@staticmethoddef what():print('This is a staticmethod')
四、类方法
class Student:school='Jlu'def __init__(self,name,age):self.name=nameself.age=agedef show(self):print(f'我叫:{self.name},今年{self.age}岁了')@staticmethoddef what():print('This is a staticmethod')@classmethod #这个@只作用于下面第一个函数def cm(cls):#cls是类方法自带的,正如实例方法自带selfprint('This is a classmethod')Student.cm() #输出This is a classmethod
类方法可以通过类名来直接调用。
五、创建类对象
stu=Student('Yorelee',21) #实例属性,使用对象名打点调用 print(stu.name,stu.age,Student.school)#实例方法,使用对象名打点调用,除了self外,需要传入所需的参数 stu.show()#类方法,使用类名打点调用,需要传入所需的参数 Student.cm()#静态方法,使用类名打点调用,需要传入所需的参数 Student.what()
六、示例
class Student:school='Jlu'def __init__(self,name,age):self.name=nameself.age=agedef show(self):print(f'我叫:{self.name},今年:{self.age}岁了')#创建对象 stu1=Student('李泽斌',21) stu2=Student('王一一',20) stu3=Student('Jack Sparrow',40) stu4=Student(age=23,name='Rose') Student.school='Haffman'#将对象存储列表当中 lst=[stu1,stu2,stu3,stu4] for i in lst:print(i.name,i.age,end='-->')i.show() print(Student.school)''' 李泽斌 21-->我叫:李泽斌,今年:21岁了 王一一 20-->我叫:王一一,今年:20岁了 Jack Sparrow 40-->我叫:Jack Sparrow,今年:40岁了 Rose 23-->我叫:Rose,今年:23岁了 Haffman '''
三、动态绑定属性和方法
stu=Student('yore',21) stu1=Student('Sweet',20)#动态绑定实例属性,直接 对象.实例属性名 stu.gender='男' #只为stu绑定了一个属性,stu1没有def introduce():print('我是一个普通的函数,我被动态绑定成stu1对象的方法')stu1.My_intro=introduce #函数赋值 introduce() stu1.My_intro()#很像是给了introduce一个别名 可以调用而已'''这里像极了普通变量 和 普通函数变量的赋值,不过不一样的是这里用了对象.'''
四、面向对象的三大特征
一、封装
单下划线开头:防君子不防小人
双下划线开头:表示私有成员,只允许本类访问。
首尾双下划线:特殊方法
class Student:def __init__(self,name,age,gender):self._name=name #受保护的,只允许本类和子类访问self.__age=age #私有的,只能类本身去访问self.gender=gender #普通实例属性,内外,子类都可以访问def _fun1(self):pass #子类和本身可以使用def __fun2(self):pass #只有定义的类可以访问def show(self): #普通的实例方法self._fun1()slef.__fun2()
与C++不一样,这里的私有成员是可以被访问的,在类外
对象名._类名__函数名/实例属性名。(不推荐使用)
print(dir(stu))#使用内置函数dir() 查看对象中的所有对象 #['_Student__age', '_Student__fun2', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_fun1', '_name', 'gender', 'show']#你会发现,私有成员的名字变成了'_Student__age',还是可以使用! #并且在实例方法里面还是可以调用私有成员,这和C++中强制不允许不一样
stu=Student('陈梅酶','21','女') print(stu._Student__age) stu._Student__fun2() #可以这样访问的原理是,
属性的设置
使用装饰器@property,可以将方法转换成属性使用,这样可以使用私有成员。但是不可以修改。
使用装饰器@属性名.setter,可以修改私有成员
class Student:def __init__(self,name):self.__name=name@propertydef name(self):#装饰器修饰之后,在使用该函数时可以不加括号return self.__name@name.setterdef name(self,value):if value=='霸道总裁':print("别搞")else:self.__name=valuestu=Student('Yore') print(stu.name) #实际上是调用方法name(),所以不能修改值 stu.name='霸道总裁' stu.name='王一一'
二、继承
子类可以使用父类的公有和保护部分内容。
如果子类只有一个父类可以使用super()来调用父类的方法。
class Person:def __init__(self,name,age):self.name=nameself.age=agedef show(self):print(f'大家好,我是{self.name}')class Student(Person):def __init__(self,name,age,stuno):super().__init__(name,age) #调用父类的初始化方法。self.stuno=stunoclass Doctor(Person):def __init__(self,name,age,deptname):super().__init__(name,age)self.deptname=deptnamestu=Student('yor',20,'0328') stu.show()doctor=Doctor('Doc.strange',20,'Com.Si.') doctor.show()
import torch import torch.nn as nn '''----------------------生成器定义代码---------------------''' class TriggerGenerator(nn.Module):#继承机器学习父类,拥有其公有内容和保护内容def __init__(self, input_size, output_size):super().__init__()#在Python3中,super()不需要指定类和selfself.fc1 = nn.Linear(input_size, 128)self.fc2 = nn.Linear(128, output_size)def forward(self, x):x = F.relu(self.fc1(x))x = self.fc2(x)return x
多继承
如果一个子类继承多个父类,用父类名来调用父类方法,需要传入self来指定子类对象。
class FatherA():def __init__(self,name):self.name=namedef showA(self):print("I am FahterA")class FatherB:def __init__(self,age):self.age=ageprint("PPAP") def showB(self):print("B")class Son(FatherA,FatherB):def __init__(self,name,age):FatherA.__init__(self,name)FatherB.__init__(self,age)son=Son('陈梅酶',20) son.showA() son.showB() ''' PPAP I am FahterA B '''
方法重写
方法重写必须方法名称和父类方法名称相同才能重写。子类重写了先调用子类的,没重写调用父类的。
class FatherB:def __init__(self,age):self.age=ageprint("PPAP")def showB(self):print("B",end=' ')class Son(FatherB):def __init__(self,name,age):FatherB.__init__(self,age)def showB(self):super().showB()print("is my father")son=Son('陈梅酶',20) son.showB() ''' PPAP B is my father '''
三、多态
C++中的多态基于继承,虚机制。但是python中只关心方法,不关心继承,只关心运行时具体是哪个对象。
class Person:def eat(self):print("人喜欢吃五谷杂粮")class Cat:def eat(self):print("猫喜欢吃鱼") class Dog:def eat(self):print("狗喜欢啃骨头")def fun(obj):obj.eat()per=Person() cat=Cat() dog=Dog()fun(per) fun(cat) fun(dog) ''' 人喜欢吃五谷杂粮 猫喜欢吃鱼 狗喜欢啃骨头 '''
五、object类
如果一个类没有父类,默认继承object类。
class Person(object):def __init__(self,name,age):self.name=nameself.age=agedef show(self):print(f'大家好,我叫:{self.name},我今年:{self.age}岁')per=Person('wq',21) #创建对象时,会自动调用__init__方法 print(dir(per)) #显示对象per的所有方法和属性,可以发现object类中的内容 ''' ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'show'] '''
__new__()先执行 创建对象,开辟空间,然后执行__init__()给对象赋值,都是自动调用的。
class Person(object):def __init__(self,name,age):self.name=nameself.age=agedef __str__(self):#方法重写return "Love U U" per=Person('l',2) print(per) #如果子类没有进行方法重写,那么默认调用__str__()返回地址,否则调用方法重写的 print(per.__str__())#也可以显式调用
六、特殊方法和特殊属性
一、特殊方法
特殊方法前后有两个下划线,与开头双下划线的私有方法不同,这种特殊方法可以在类外调用。
a=10 #Python当中一切皆对象 print(dir(10)) print(a+10) print(a.__add__(10)) print(a.__sub__(1)) #1.__sub__(1)是不行的 ''' ['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__getstate__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_count', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes'] 20 20 '''
二、特殊属性
obj.表示对象调用,实例属性。class.表示类调用,类属性。
层次结构表示的是都继承了谁,父类默认为第一个。
子类列表是方法,返回值为一个列表,并且只返回直属的儿子,孙子不在里面。
class A:school='JJ' class B:pass class D:school='J' class C(A,B,D):def __init__(self,name,age):self.name=nameself.age=agea=A() b=B() c=C('Yore',20)print('对象a的实例属性字典:',a.__dict__) print('对象b的实例属性字典:',b.__dict__) print('对象c的实例属性字典:',c.__dict__) ''' 对象a的实例属性字典: {} 对象b的实例属性字典: {} 对象c的实例属性字典: {'name': 'Yore', 'age': 20} '''print('对象a所属的类:',a.__class__) print('对象b所属的类:',b.__class__) print('对象c所属的类:',c.__class__) ''' 对象a所属的类: <class '__main__.A'> 对象b所属的类: <class '__main__.B'> 对象c所属的类: <class '__main__.C'> '''print(A.__bases__,A.__base__) print(B.__bases__,B.__base__) print(C.__bases__,C.__base__) ''' (<class 'object'>,) <class 'object'> (<class 'object'>,) <class 'object'> (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>) <class '__main__.A'> ''' print(A.__mro__) print(B.__mro__) print(C.__mro__) ''' (<class '__main__.A'>, <class 'object'>) (<class '__main__.B'>, <class 'object'>) (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class 'object'>) ''' object.__subclasses__() A.__subclasses__() C.__subclasses__() ''' [<class 'type'>, <class 'async_generator'>, <class 'bytearray_iterator'>, <class 'bytearray'>, <class 'bytes_iterator'>, <class 'bytes'>, <class 'builtin_function_or_method'>, <class 'callable_iterator'>, <class 'PyCapsule'>, <class 'cell'>, <class 'classmethod_descriptor'>, <class 'classmethod'>, <class 'code'>, <class 'complex'>, <class '_contextvars.Token'>, <class '_contextvars.ContextVar'>, <class '_contextvars.Context'>, <class 'coroutine'>, <class 'dict_items'>, <class 'dict_itemiterator'>, <class 'dict_keyiterator'>, <class 'dict_valueiterator'>, <class 'dict_keys'>, <class 'mappingproxy'>, <class 'dict_reverseitemiterator'>, <class 'dict_reversekeyiterator'>, <class 'dict_reversevalueiterator'>, <class 'dict_values'>, <class 'dict'>, <class 'ellipsis'>, <class 'enumerate'>, <class 'filter'>, <class 'float'>, <class 'frame'>, <class 'frozenset'>, <class 'function'>, <class 'generator'>, <class 'getset_descriptor'>, <class 'PyHKEY'>, <class 'instancemethod'>, <class 'list_iterator'>, <class 'list_reverseiterator'>, <class 'list'>, <class 'longrange_iterator'>, <class 'int'>, <class 'map'>, <class 'member_descriptor'>, <class 'memoryview'>, <class 'method_descriptor'>, <class 'method'>, <class 'moduledef'>, <class 'module'>, <class 'odict_iterator'>, <class 'pickle.PickleBuffer'>, <class 'property'>, <class 'range_iterator'>, <class 'range'>, <class 'reversed'>, <class 'symtable entry'>, <class 'iterator'>, <class 'set_iterator'>, <class 'set'>, <class 'slice'>, <class 'staticmethod'>, <class 'stderrprinter'>, <class 'super'>, <class 'traceback'>, <class 'tuple_iterator'>, <class 'tuple'>, <class 'str_iterator'>, <class 'str'>, <class 'wrapper_descriptor'>, <class 'zip'>, <class 'types.GenericAlias'>, <class 'anext_awaitable'>, <class 'async_generator_asend'>, <class 'async_generator_athrow'>, <class 'async_generator_wrapped_value'>, <class 'Token.MISSING'>, <class 'coroutine_wrapper'>, <class 'generic_alias_iterator'>, <class 'items'>, <class 'keys'>, <class 'values'>, <class 'hamt_array_node'>, <class 'hamt_bitmap_node'>, <class 'hamt_collision_node'>, <class 'hamt'>, <class 'InterpreterID'>, <class 'managedbuffer'>, <class 'memory_iterator'>, <class 'method-wrapper'>, <class 'types.SimpleNamespace'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'str_ascii_iterator'>, <class 'types.UnionType'>, <class 'weakref.CallableProxyType'>, <class 'weakref.ProxyType'>, <class 'weakref.ReferenceType'>, <class 'EncodingMap'>, <class 'fieldnameiterator'>, <class 'formatteriterator'>, <class 'BaseException'>, <class '_frozen_importlib._ModuleLock'>, <class '_frozen_importlib._DummyModuleLock'>, <class '_frozen_importlib._ModuleLockManager'>, <class '_frozen_importlib.ModuleSpec'>, <class '_frozen_importlib.BuiltinImporter'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib._ImportLockContext'>, <class '_thread.lock'>, <class '_thread.RLock'>, <class '_thread._localdummy'>, <class '_thread._local'>, <class '_io._IOBase'>, <class '_io.IncrementalNewlineDecoder'>, <class '_io._BytesIOBuffer'>, <class 'nt.ScandirIterator'>, <class 'nt.DirEntry'>, <class '_frozen_importlib_external.WindowsRegistryFinder'>, <class '_frozen_importlib_external._LoaderBasics'>, <class '_frozen_importlib_external.FileLoader'>, <class '_frozen_importlib_external._NamespacePath'>, <class '_frozen_importlib_external.NamespaceLoader'>, <class '_frozen_importlib_external.PathFinder'>, <class '_frozen_importlib_external.FileFinder'>, <class 'codecs.Codec'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class '_abc._abc_data'>, <class 'abc.ABC'>, <class 'collections.abc.Hashable'>, <class 'collections.abc.Awaitable'>, <class 'collections.abc.AsyncIterable'>, <class 'collections.abc.Iterable'>, <class 'collections.abc.Sized'>, <class 'collections.abc.Container'>, <class 'collections.abc.Callable'>, <class '_winapi.Overlapped'>, <class 'os._wrap_close'>, <class 'os._AddedDllDirectory'>, <class '_sitebuiltins.Quitter'>, <class '_sitebuiltins._Printer'>, <class '_sitebuiltins._Helper'>, <class '_multibytecodec.MultibyteCodec'>, <class '_multibytecodec.MultibyteIncrementalEncoder'>, <class '_multibytecodec.MultibyteIncrementalDecoder'>, <class '_multibytecodec.MultibyteStreamReader'>, <class '_multibytecodec.MultibyteStreamWriter'>, <class '_virtualenv._Finder'>, <class '_distutils_hack._TrivialRe'>, <class '_distutils_hack.DistutilsMetaFinder'>, <class '_distutils_hack.shim'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>] [<class '__main__.C'>] [] '''
七、类的拷贝
变量的赋值:A=B,该变量A的指针直接指向B指向的内存空间
浅拷贝:A=copy.copy(B),给A开辟一个新的空间,A指向该空间,该空间中的内容完全拷贝了B指向的空间的内容。
深拷贝:A=copy.deepcopy(B),给A给A开辟一个新的空间,A指向该空间,该空间中的对象同样开辟新的空间。
红色com1是赋值
蓝色com2是浅拷贝
橙色com3是深拷贝
一、变量的赋值
普通的不可变数据类型的赋值,只会有一个对象,所有变量指向同一个变量。
可变组合数据类型会创建一个副本,赋值之后产生一个新的对象,该对象指向新对象。
通过以下例子你会发现,com和com1指向的地址是一样的,给com1.a赋值之后,com.a的值也发生了变化,说明com和com1真的指向的是同一个内存地址,本质上属于同一个变量。
class A:pass class B:pass class Computer:def __init__(self,a,b):self.a=aself.b=ba=A() b=B() a1=A()com=Computer(a,b) com1=com print(com,com.a,com.b) print(com1,com1.a,com1.b) com1.a=a1 print(com1,com1.a,com1.b) print(com,com.a,com.b) ''' <__main__.Computer object at 0x0000020FB8E48D50> <__main__.A object at 0x0000020FB8E48C90> <__main__.B object at 0x0000020FB8E48CD0> <__main__.Computer object at 0x0000020FB8E48D50> <__main__.A object at 0x0000020FB8E48C90> <__main__.B object at 0x0000020FB8E48CD0> <__main__.Computer object at 0x0000020FB8E48D50> <__main__.A object at 0x0000020FB8E48D10> <__main__.B object at 0x0000020FB8E48CD0> <__main__.Computer object at 0x0000020FB8E48D50> <__main__.A object at 0x0000020FB8E48D10> <__main__.B object at 0x0000020FB8E48CD0> '''lst1=[1,2,3] lst2=[1,2,3] print(id(lst1),id(lst2)) lst2.append(4) print(id(lst2)) ''' 1377912245248 1377913237952 1377913237952 '''t1=tuple([1,2,3]) t2=t1 print(id(t1),id(t2))
二、浅拷贝
使用copy模块,子对象类型不拷贝。也就是说这个时候两个变量指向的是两个不同的对象,但是原来的对象中的对象仍然指向同一个对象。
import copy class A:pass class B:pass class Computer:def __init__(self,a,b):self.a=aself.b=b a=A() b=B()com=Computer(a,b) com1=copy.copy(com) print(com,com.a,com.b) print(com1,com1.a,com1.b) ''' <__main__.Computer object at 0x0000019D2E6793D0> <__main__.A object at 0x0000019D2E679210> <__main__.B object at 0x0000019D2E678A50> <__main__.Computer object at 0x0000019D2E67A7D0> <__main__.A object at 0x0000019D2E679210> <__main__.B object at 0x0000019D2E678A50> '''
com和com1指向了内存中不同的对象,内存地址不同,但是他们中的对象仍然指向的是同一个对象。相当于给com1开辟了一个新的空间,里面的内容仍然完全copy了com的内容。
三、深拷贝
import copy class A:pass class B:pass class Computer:def __init__(self,a,b):self.a=aself.b=b a=A() b=B()com=Computer(a,b) com1=copy.deepcopy(com) print(com,com.a,com.b) print(com1,com1.a,com1.b) ''' <__main__.Computer object at 0x0000028D1A3B93D0> <__main__.A object at 0x0000028D1A3B9210> <__main__.B object at 0x0000028D1A3B8A90> <__main__.Computer object at 0x0000028D1A3BBC10> <__main__.A object at 0x0000028D1A3D3FD0> <__main__.B object at 0x0000028D1A3D3E50>'''