Python----类对象和实例对象

目录

一.类和类的实例

二.类属性和实例属性

三.私有属性和公有属性

四.静态方法和类方法

五.__init__方法,__new__方法和__del__方法:

六.私有方法和公有方法

七.方法的重载

八.方法的继承

九.方法的重写

十.对象的特殊方法

十一.对象的引用,浅拷贝和深拷贝


一.类和类的实例

类(Class):用来描述具有相同的属性和方法的对象的集合。定义了该集合中每个对象所共有的属性和方法。

类的实例:每个对象都属于特定的类,并被称为该类的实例(类的具体实体)。

看点实际的:

python 复制代码
class Person1: #定义类Person1
    pass  # 类体为空语句
print(Person1, type(Person1), id(Person1))

结果:
<class '__main__.Person1'> <class 'type'> 2077119090192

Person1:表示类对象的名称,属于"main"模块

type(Person1):表示Person1一个类

id(Person1):表示Person1类的唯一标识符,用来区别其他对象

python 复制代码
class Person1: #定义类Person1
    pass  # 类体为空语句
p1 = Person1() #实例化该类并创建一个对象p1
print(p1, type(p1),  id(p1))

结果:
<__main__.Person1 object at 0x0000026DD4176DD0> <class '__main__.Person1'> 2670732996048

p1:p1是Person1类的一个对象,属于"main"模块,其内存地址为0x0000026DD4176DD0

type(p1):表示p1所属的类是Person1,属于"main"模块

id(p1):表示p1对象的唯一标识符

二.类属性和实例属性

类变量(属性):类变量在整个实例化的对象中是公用的, 定义在类中且在函数体之外,通常不作为实例变量使用,属于类本身,可以通过类名访问/修改

python 复制代码
class Person2:
    count = 0         #定义属性count,表示计数
    name = "Person"   #定义属性name,表示名称
    def __init__(self):
        pass
    def fun(self):
        pass
#测试代码
Person2.count += 1     #通过类名访问,计数加1
print(Person2.count)   #类名访问,读取并显示类属性
print(Person2.name)    #类名访问,读取并显示类属性

结果:
1
Person

实例变量:在类的声明中,属性是用变量来表示的,定义在方法内,比如定义到构造方法中,普通的方法内,通过self.变量名定义的属性。

python 复制代码
class Person3:                 #定义类Person3
    def __init__(self, name,age): #__init__方法
        self.name = name          #初始化self.name,
        self.age = age            #初始化self.age,
    def say_hi(self):         #定义类Person3的函数say_hi()
        print('您好, 我叫', self.name) 
#测试代码
p1 = Person3('张三',25)    #对象实例化
p1.say_hi ()               #调用对象的方法
print(p1.age)    #通过p1.age(obj1.变量名)读取成员变量age

结果:
您好,我叫张三
25

改变类属性值或实例变量值

python 复制代码
class Person4:
    count = 0         #定义属性count,表示计数
    name = "Person"   #定义属性name,表示名称
#测试代码
p1 = Person4()         #创建实例对象1
p2 = Person4()         #创建实例对象2
print((p1.name, p2.name))   #通过实例对象访问实例变量
Person4.name = "雇员"     #通过类变量访问,设置类属性值
print((p1.name, p2.name))   #读取实例变量
p1.name = "员工"    #通过实例变量访问,设置实例变量的值
print((p1.name, p2.name))   #读取实例变量的值

结果:

三.私有属性和公有属性

Python类的成员没有访问控制限制

约定两个下划线 开头,但不以两个下划线结束的属性是私有的(private),其他为公共的(public)

python 复制代码
class A:
    def __init__(self):
        self.__name = 'class A'   #私有类属性

    def get_name(self):
        print(self.__name)         #在类方法中访问私有类属性
#测试代码
Aa = A()
Aa.get_name()
Aa.__name          #导致错误,不能直接访问私有类属性

结果:

@property

面向对象程序设计的封装性原则要求不直接访问类中的数据成员@property装饰器装饰访问私有属性的方法,使访问方式更友好。

python 复制代码
class Person1:
    def __init__(self, name):
        self.__name = name
    @property
    def name(self):
        return self.__name
#测试代码
p = Person1('王五')
print(p.name)

结果:
王五

@property装饰器默认提供一个只读属性 ,可使用对应的getter、setter和deleter装饰器实现其他访问器函数 ,@property装饰器会将方法转换为相同名称的只读属性 ,可以与所定义的属性配合使用,这样可以防止属性被修改

python 复制代码
class Person12:
    def __init__(self, name):
        self.__name = name
    @property
    def name(self):
        return self.__name
    @name.setter
    def name(self, value):
        self.__name = value
    @name.deleter
    def name(self):
        del self.__name
#测试代码
p = Person12('姚六')
p.name = '王依依'
print(p.name)

结果:
王依依

property的调用格式

property(fget=None, fset=None, fdel=None, doc=None) #fget为get访问器,fset为set访问器,fdel为del访问器

python 复制代码
class Person1:
    def __init__(self, name):
        self.__name = name
    def getname(self):
        return self.__name
    def setname(self, value):
        self.__name = value
    def delname(self):
        del self.__name
    name = property(getname, setname, delname, "I'm the 'name' property.")
#测试代码
p = Person1('张三')
print(p.name)
p.name = '李四'
print(p.name)

结果:
张三
李四

Python对象包含许多以双下划线开始和结束的变量,称为特殊属性 :

|------------------------|-------------|---------------------------------------------------------------------|
| 特殊方法 | 含义 | 示例 |
| object.dict | 对象的属性字典 | int.dict #mappingproxy({'new': <built-in method new... |
| instance.class | 对象所属的类 | i.class #<class 'int'> |
| instance.class | 对象所属的类 | int.class #<class 'type'> |
| class.bases | 类的基类元组 | int.bases #(<class 'object'>) |
| class.base | 类的基类 | int.base #<class 'object'> |
| class.name | 类的名称 | int.name # 'int' |
| class.qualname | 类的限定名称 | int.qualname #'int' |
| class.mro | 查看继承关系,基类元组 | int.mro #(<class 'int'>, <class 'object'>) |
| class.mro() | 同上,可被子类重写 | int.mro()#[<class 'int'>, <class 'object'>] |
| class.subclasses() | 子类列表 | int.subclasses()#[<class 'bool'>,<enum 'IntEnum'>,... |

自定义属性:

对象可以通过特殊属性**dict**存储自定义属性:

python 复制代码
class C1:
    pass
o=C1()
o.name='custom name'
print(o.__dict__)

结果:

拦截属性的访问:

可通过重载__getattr__和__setattr__拦截对成员的访问,从而自定义属性的行为

**getattr**只有在访问不存在的成员时才会调用

**getattribute**拦截所有(包括不存在的成员)的获取操作

注意:不要使用return self.dict[name]返回结果,因为self.dict[name]同样会被__getattribute__拦截,造成无限递归死循环

getattr(self, name) #获取属性,比__getattribute__()优先调用

getattribute(self, name)# 获取属性

setattr(self, name, value)#设置属性

delattr(self,name) #删除属性

python 复制代码
class CustomAttribute(object):
    def __init__(self): #__init__方法(构造函数)
        pass		    #空语句

    def __getattribute__(self, name): #获取属性,拦截所有的获取操作
        return str.upper(object.__getattribute__(self, name))#转换为大写

    def __setattr__(self, name, value):  #设置属性
        object.__setattr__(self, name, str.strip(value))#去除收尾空格
#测试代码
o = CustomAttribute()     #创建实例对象
o.firstname='   mary  '   #设置成员变量的值
print(o.firstname)        # 读取并显示成员变量的值

结果:

四.静态方法和类方法

静态方法

•声明与类的对象实例无关的方法

•静态方法不对特定实例进行操作,访问对象实例会导致错误

•静态方法通过装饰器**@staticmethod来**定义

•静态方法一般通过类名 来访问,也可以通过对象实例调用

静态方法的声明和调用

#声明

@staticmethod

def 静态方法名([形参列表])

函数体

#调用

类名.静态方法名([实参列表])

python 复制代码
class TemperatureConverter:
    @staticmethod
    def c2f(t_c): #摄氏温度到华氏温度的转换
            t_c = float(t_c)
            t_f = (t_c * 1.8) + 32
            return t_f
    @staticmethod
    def f2c(t_f): #华氏温度到摄氏温度的转换
            t_f = float(t_f)
            t_c = (t_f - 32) /1.8
            return t_c
#测试代码
print("1. 从摄氏温度到华氏温度.")
print("2. 从华氏温度到摄氏温度.")
choice = int(input("请选择转换方向:"))
if choice == 1:
    t_c = float(input("请输入摄氏温度: "))
    t_f = TemperatureConverter.c2f(t_c)
    print("华氏温度为: {0:.2f}".format(t_f))
elif choice == 2:
    t_f = float(input("请输入华氏温度: "))
    t_c = TemperatureConverter.f2c(t_f)
    print("摄氏温度为: {0:.2f}".format(t_c))
else:
    print("无此选项,只能选择1或2!")

类方法

•Python允许声明属于类本身的方法,即类方法

•类方法不对特定实例进行操作,访问对象实例会导致错误

•类方法通过装饰器**@classmethod**来定义

第一个形式参数必须为类对象本身,通常为cls

类方法的声明和调用

#声明

@classmethod

def 类方法名(cls,[形参列表])

函数体

#调用

类名.类方法名([实参列表])

python 复制代码
class Foo:
    classname = "Foo"
    def __init__(self, name):
        self.name = name
    def f1(self):           #实例方法
        print(self.name)
    @staticmethod
    def f2():               #静态方法
        print("static")
    @classmethod
    def f3(cls):            #类方法
        print(cls.classname)
#测试代码
f = Foo("李")
f.f1()
Foo.f2()
Foo.f3()

结果:

五.__init__方法,__new__方法和__del__方法:

__init__方法即构造函数(构造方法),用于执行类实例的初始化。创建完对象后调用,初始化当前对象的实例,无返回值

__new__方法是一个类方法,创建对象时调用,返回当前对象的一个实例,一般无需重载该方法

python 复制代码
#__init__方法
class Point:
    def __init__(self, x = 0, y = 0): #构造函数
        self.x = x
        self.y = y
p1 = Point()                   #创建对象
print("p1({0},{1})".format(p1.x, p1.y))
p1 = Point(5, 5)               #创建对象
print("p1({0},{1})".format(p1.x, p1.y))

结果:
p1(0,0)
p1(5,5)

•__del__方法:

__del__方法即析构函数(析构方法),用于实现销毁类的实例所需的操作,如释放对象占用的非托管资源(例如:打开的文件、网络连接等)

默认情况下,当对象不再被使用时,__del__方法运行,由于Python解释器实现自动垃圾回收,即无法保证这个方法究竟在什么时候运行

通过del语句,可以强制销毁一个对象实例,从而保证调用对象实例的__del__方法

python 复制代码
class Person:
    count = 0                #定义类域count,表示计数
    def __init__(self, name,age): #构造函数
        self.name = name 
        self.age = age    
        Person.count += 1   #创建一个实例时,计数加1
    def __del__(self):      #析构函数
        Person.count -= 1   #销毁一个实例时,计数减1
    def get_count():        #定义类Person的方法get_count()
        print('总计数为:', Person.count)
print('总计数为:',Person.count) 	#类名访问
p1 = Person('张三',25)    	#创建对象
Person.get_count()  		#通过类名访问
p2 = Person('李四',28)   	#创建对象
Person.get_count()  		#通过类名访问
del p1            		#删除对象p1
Person.get_count()  		#通过类名访问
del p2            		#删除对象p2
Person.get_count()  		#通过类名访问

结果:

六.私有方法和公有方法

两个下划线开头,但不以两个下划线结束的方法是私有的(private),其他为公共的(public)

•以双下划线开始和结束的方法是Python的专有特殊方法。

不能直接访问私有方法,但可以在其他方法中访问

python 复制代码
class Book:
    def __init__(self, name, author, price):
        self.name = name
        self.author = author
        self.price = price
    def __check_name(self):  #定义私有方法,判断name是否为空
        if self.name == '' : return False
        else: return True
    def get_name(self):      #定义类Book的方法get_name
        if self.__check_name():print(self.name,self.author) #调用私有方法
        else:print('No value')
b = Book('Python语言程序设计','嵩天',50.0)    #创建对象
b.get_name()                    #调用对象的方法
b.__check_name()                

结果:

七.方法的重载

•其他程序语言方法可以重载,即定义多个重名的方法,而方法签名唯一(方法名、参数数量和参数类型)

•Python本身是动态语言 ,方法的参数没有声明类型 (在调用传值时确定参数的类型),参数的数量可变 。故Python对象方法不需要重载,定义一个方法即可实现多种调用,从而实现相当于其他程序设计语言的重载功能

python 复制代码
class Person21:                #定义类Person21
    def say_hi(self, name=None): #定义类方法say_hi
        self.name = name 
        if name==None: print('您好! ')
        else: print('您好, 我叫', self.name)
p21 = Person21()           #创建对象
p21.say_hi()               #调用对象的方法,无参数
p21.say_hi('威尔逊')        #调用对象的方法,带参数

结果:

再看以下代码:

在Python类体中可以定义多个重名的方法,虽然不会报错,但只有最后一个方法有效,所以建议不要定义重名的方法

python 复制代码
class Person22:
    def say_hi(self, name):    #带两个参数
        print('您好, 我叫', self.name)
    def say_hi(self, name, age): #带三个参数
        print('hi, {0}, 年龄:{1}'.format(name,age))
p22 = Person22()               #创建对象
p22.say_hi('Lisa', 22)         #调用对象的方法
p22.say_hi('Bob')              # error

结果:

八.方法的继承

•派生类:Python支持多重继承,即一个派生类可以继承多个基类

•如果类定义中没有指定基类,默认基类为object。object是所有对象的根基类

•声明派生类时,必须在其构造函数中调用基类的构造函数

派生类的声明和调用:

#声明

class 派生类名(基类1,[基类2...]):

类体

#调用

基类名.init(self, 参数列表)

或者:

super().init(参数列表)

python 复制代码
class Person:                 #基类
    def __init__(self, name, age): #构造函数
        self.name = name
        self.age = age
    def say_hi(self):         #定义基类方法say_hi
        print('您好, 我叫{0}, {1}岁'.format(self.name, self.age))
class Student(Person):         #派生类
    def __init__(self, name, age, stu_id): #构造函数
        Person.__init__(self, name, age) #调用基类构造函数
        self.stu_id = stu_id    #学号
    def say_hi(self):          #定义派生类方法say_hi
        Person.say_hi(self)    #调用基类方法say_hi
        print('我是学生, 我的学号为:', self.stu_id)
p1 = Person('张王一', 33)            #创建对象
p1.say_hi()
s1 = Student('李姚二', 20, '2018101001') #创建对象
s1.say_hi()

结果:

通过类的方法mro() 或类的属性**mro**可以输出其继承的层次关系

>>> class A: pass

>>> class B(A):pass

>>> class C(B):pass

>>> class D(A):pass

>>> class E(B,D):pass

>>> D.mro()

\, \, \

>>> E.mro

(<class 'main.E'>, <class 'main.B'>, <class 'main.D'>, <class 'main.A'>, <class 'object'>)

九.方法的重写

通过继承,派生类继承基类中除构造方法之外的所有成员,如果在派生类中重新定义从基类继承的方法,则派生类中定义的方法覆盖从基类中继承的方法

python 复制代码
class Dimension:  #定义类Dimensions
    def __init__(self, x, y): #构造函数
        self.x = x       #x坐标
        self.y = y       #y坐标
    def area(self):       #基类的方法area()
        pass
class Circle(Dimension):  #定义类Circle(圆)
    def __init__(self, r): #构造函数
        Dimension.__init__(self, r, 0)
    def area(self):        #覆盖基类的方法area()
        return 3.14 * self.x * self.x   #计算圆面积
class Rectangle(Dimension):  #定义类Rectangle(矩形)
    def __init__(self, w, h): #构造函数
        Dimension.__init__(self, w, h)
    def area(self):  #覆盖基类的方法area()
        return self.x * self.y  #计算矩形面积
d1 = Circle(2.0)          #创建对象:圆
d2 = Rectangle(2.0, 4.0)   #创建对象:矩形
print(d1.area(), d2.area())  #计算并打印圆和矩形面积

结果:

十.对象的特殊方法

|------------------------------|-----------------|
| 特殊方法 | 含义 |
| lt、__add__等 | 对应运算符<,+等 |
| initdel | 创建或销毁对象时调用 |
| len | 对应内置函数len() |
| setitemgetitem | 按索引赋值、取值 |
| repr(self) | 对应于内置函数repr() |
| str(self) | 对应于内置函数str() |
| bytes(self) | 对应于内置函数bytes() |
| format(self,format_spec) | 对应于内置函数format() |
| bool(self) | 对应于内置函数bool() |
| hash(self) | 对应于内置函数hash() |
| dir(self) | 对应于内置函数dir() |

对象的特殊方法实例:

python 复制代码
class Person:
    def __init__(self, name, age): #特殊方法(构造函数)
        self.name = name
        self.age = age
    def __str__(self):          #特殊方法
        return '{0}, {1}'.format(self.name,self.age)
    def __repr__(self):         #特殊方法
        return '{},{}'.format('李四','24')
#测试代码
p1 = Person('张三', 23)
print(p1)
print(repr(p1))

结果:
张三,23
李四,24

运算符的重载与对象的特殊方法

Python的运算符实际上是通过调用对象的特殊方法实现的

|---------------------|--------------------------------------------------------------------------------------------------------|-------------|
| 运算符 | 特殊方法 | 含义 |
| <,<=,==,>,>=,!= | lt(),le(),eq(),gt() ,ge(),ne() | 比较运算符 |
| |,^,& | or(),ror(),xor(), rxor(),and(),rand() | 按位或、异或、与 |
| |=,^=,&= | ior(),ixor(),iand() | 按位复合赋值运算 |
| <<,>> | lshift(),rlshift(),rshift(),rrshift() | 移位运算 |
| <<=,>>= | ilshift(),irlshift(),irshift(),irrshift() | 移位复合赋值运算 |
| +,- | add(),radd(),sub(),rsub() | 加法与减法 |
| +=,-+ | iaddr(),isub() | 加减复合赋值运算 |
| *,/,%,// | mul(),rmul(),truediv(),rtruediv(), mod(),rmod(),floordiv(),rfloordiv() | 乘、除、取余、整数除法 |
| *=,/=,%=.//= | imul(),idiv(),itruediv(),imod(),ifloordiv() | 乘除复合赋值运算 |
| +x,-x | pos(),neg() | 正负号 |
| ~x | invert() | 按位翻转 |
| **, **= | pow(),rpow(),ipow() | 指数运算 |

示例:

python 复制代码
class MyList():
    def __init__(self, *args):
        self.__mylist = []                 #初始化私有属性,空列表
        for i in args:
            self.__mylist.append(i)
    def __add__(self, other):              #重载运算符"+",每个元素增加n
        for i in range(len(self.__mylist)):
            self.__mylist[i] += other
    def __sub__(self, other):              #重载运算符"-",每个元素减少n
        for i in range(len(self.__mylist)):
            self.__mylist[i] -= other
    def __mul__(self, other):
        for i in range(len(self.__mylist)):
            self.__mylist[i] *= other
    def __truediv__(self, other):
        for i in range(len(self.__mylist)):
            self.__mylist[i] /= other
    def __len__(self):
        return len(self.__mylist)
    def __repr__(self):
        str1 = ''
        for i in range(len(self.__mylist)):
            str1 += str(self.__mylist[i])+' '
        return str1
alist = MyList(1,2,3,4,5);
alist + 2;
print(repr(alist));
alist - 1;
print(repr(alist));
alist * 2;
print(repr(alist));
alist/2;print(repr(alist));
print(len(alist))

@functools.total_ordering:

支持大小比较的对象需要实现特殊方法:eqltlegegt,但使用functools模块的total_ordering装饰器装饰类,则只需要实现**eq** ,以及**ltlege、__gt__中的任意一个**

python 复制代码
import functools
@functools.total_ordering
class Student:
    def __init__(self, firstname, lastname):  #姓和名
        self.firstname = firstname
        self.lastname = lastname
    def __eq__(self, other):     #判断姓名是否一致
        return ((self.lastname.lower(), self.firstname.lower()) ==
                (other.lastname.lower(), other.firstname.lower()))
    def __lt__(self, other):     #self姓名<other姓名
        return ((self.lastname.lower(), self.firstname.lower()) <
                (other.lastname.lower(), other.firstname.lower()))
#测试代码
if __name__ == '__main__':
    s1 = Student('Mary','Clinton')
    s2 = Student('Mary','Clinton')
    s3 = Student('Charlie','Clinton')
    print(s1==s2)
    print(s1>s3)
    print(s1<s3)

结果:

__call__方法:

__call__方法的对象称之为可调用对象(callable),即该对象可以像函数一样被调用

python 复制代码
class GDistance:       #类:自由落体距离
    def __init__(self, g): #构造函数
        self.g = g 
    def __call__(self, t): #自由落体下落距离
        return (self.g*t**2)/2
#测试代码
if __name__ == '__main__':
    e_gdist = GDistance(9.8) #地球上的重力加速度
    for t in range(11):   #自由落体0~10秒的下落距离
        print('下落距离为{:0.2f}m'.format(e_gdist(t))) 

结果:

十一.对象的引用,浅拷贝和深拷贝

对象的引用:在创建一个对象并赋值给一个变量时,该变量是指向该对象的引用,其id()返回值保持一致

python 复制代码
>>> acc10=['Charlie', ['credit', 0.0]] 
#创建列表对象(信用卡账户),变量acc10代表主卡
>>> acc11=acc10                                 
#变量acc11代表副卡,指向acc10(主卡)的对象
>>> id(acc10),id(acc11)                      
#二者id相同,输出:
(2739033039112, 2739033039112)

对象的浅拷贝:

对象的拷贝可以使用以下方法

切片操作,例如,acc11[:]。

对象实例化,例如,list(acc11)。

copy模块的copy函数,例如,copy.copy(acc1)。

python 复制代码
import copy
acc1=['Charlie',['credit',0.0]]
acc2=acc1[:]  #使用切片方式拷贝对象
acc3=list(acc1)  #使用对象实例化方法拷贝对象
acc4=copy.copy(acc1) #使用copy.copy函数拷贝对象
print(id(acc1),id(acc2),id(acc3),id(acc4)) #拷贝对象id各不相同
acc2[0]='Mary'  #acc2的第一个元素赋值,即户主为'Mary'
acc2[1][1]=-99.9 #acc2的第二个元素的第二个元素赋值,即消费金额99.9
print(acc1, acc2)           #注意,acc2消费金额改变99.9,acc1也随之改变

结果:

对象的深拷贝

Python复制一般为浅拷贝,即复制对象时对象中包含的子对象并不复制,而是引用同一个子对象。如果要递归复制对象中包含的子对象,需使用深拷贝

深拷贝需要使用copy模块的deepcopy函数,拷贝对象中包含的子对象

>>> acc5[0]='Clinton' #acc5的第1个元素赋值,即户主为'Clinton'

>>> acc5[1][1]=-19.9 #acc5的第2个元素的第2个元素赋值,即消费金额19.9

>>> acc1,acc5

(['Charlie', ['credit', 0.0]], ['Clinton', ['credit', -19.9]])

>>> id(acc1),id(acc5),id(acc1[1]),id(acc5[1])

(2739033040648, 2739033040264, 2739033040520, 2739033039688)

相关推荐
lightqjx2 分钟前
【数据结构】复杂度分析
c语言·开发语言·数据结构·算法
sohoAPI8 分钟前
Flask快速入门
后端·python·flask
程序员小白条1 小时前
我的第二份实习,学校附近,但是干前端!
java·开发语言·前端·数据结构·算法·职场和发展
钟琛......2 小时前
java中父类和子类的成员变量可以重名吗
java·开发语言
沐知全栈开发2 小时前
PHP 超级全局变量
开发语言
Deng9452013145 小时前
基于Python的职位画像系统设计与实现
开发语言·python·文本分析·自然语言处理nlp·scrapy框架·gensim应用
一只小青团8 小时前
Python之面向对象和类
java·开发语言
qq_529835358 小时前
ThreadLocal内存泄漏 强引用vs弱引用
java·开发语言·jvm
景彡先生8 小时前
C++并行计算:OpenMP与MPI全解析
开发语言·c++
FreakStudio9 小时前
一文速通 Python 并行计算:13 Python 异步编程-基本概念与事件循环和回调机制
python·pycharm·协程·多进程·并行计算·异步编程