python类和对象

目录

1.类和对象的含义

2.定义类和创建对象

定义类

创建对象和调用成员

默认初始化

self 参数

类的构造函数和析构函数

3.有名对象和匿名对象

4.不同对象的属性和方法的地址

5.对象的引用与拷贝

对象赋值是引用

用拷贝保证数据安全

6.数据私有化

7.动态增加方法

8.运算符重载

9.继承

单继承

多重继承

私有属性和方法不可继承

object 类

多态和动态绑定

10.isinstance 函数

11.静态方法


1.类和对象的含义

你可以根据一张苹果派食谱(类)制作出任意多个苹果派(对象)
类就像模板一样,对象是具体的实例
对象是类的一个实例,你可以创建同种类的多个对象
对象就是实例,实例就是对象
类中具有两类成员:是
  • 数据域 => 属性
  • 行为 => 方法
例子:可以把人看做一种类,张三、李四等就是具体的对象,具体的实例,人的身高、体重、年龄等值就是具体的属性,数据域,人吃饭、睡觉、跑步等动作就是对象的行为,对象所具有的方法。

2.定义类和创建对象

定义类

定义类语法
class ClassName:
    initializer
    methods
类名 ClassName 一般采用首字母大写的习惯,驼峰命名法。
特殊方法对类初始化
使用类的特殊方法:__init__(左右各是两根下划线,中间是 init)来初始化
初始化程序,用来完成一系列初始化动作,用来设置对象的初始属性
例子:定义一个圆类
import math


class Circle:
    def __init__(self, radius=1):
        self.radius = radius

    def get_perimeter(self):
        return 2 * self.radius * math.pi

    def get_area(self):
        return math.pi * (self.radius ** 2)

    def set_radius(self, radius):
        self.radius = radius

在上面的这个例子中,定义了一个Circle类:

  • 如果需要创建基于该类的对象,需要传递参数radius,默认值为1。
  • 在创建对象时,会进行初始化操作,生成一个成员self.radius,其初始值为传递的参数值radius。
  • 另外还对该类定义了三个方法get_perimeter,get_area,set_radius
  • self代表类本身。比如self.radius表示该类所具有的属性成员,而radius是形参等外部成员或者局部变量。

创建对象和调用成员

创建对象语法:object_var = ClassName(参数)

调用成员:使用圆点运算符(.)

# 创建一个圆对象
c1 = Circle(2)  # 类名(参数 1,参数 2,参数 n)

# 访问圆对象的数据域
r1 = c1.radius
print(r1)  # 2

# 调用对象的方法
area1 = c1.get_area()
print(area1)  # 12.566370614359172

默认初始化

如果不使用__init__特殊方法,系统会默认进行初始化
import math


class Circle:
    radius = 1
    width = 5
    height = 10

    def get_perimeter(self):
        return 2 * self.radius * math.pi

    def get_area(self):
        return math.pi * (self.radius ** 2)

    def set_radius(self, radius):
        self.radius = radius


c1 = Circle()
print(c1.radius)  # 1
print(c1.width)   # 5
print(c1.height)  # 10 

在这个例子中,尽管radius = 1 ,width = 5 和height = 10前面没有加self,但是由于没有定义__init__特殊方法,系统默认进行了初始化,生成了三个成员变量,self.radius,self.width,self.height。

self 参数

self = 对象本身

在类的内部使用self.x 访问实例的属性 x,使用语法 self.method()来调用实例的方法 method()

import math


class Circle:
    radius = 1
    width = 5
    height = 10

    def get_area(self):
        return math.pi * (self.radius ** 2)

    def set_radius(self, radius):
        self.radius = radius
    
    def func(self):
        self.set_radius(2)
        ret = self.get_area()  
        print(ret)  # # 12.566370614359172


c1 = Circle()
c1.func()  

类的构造函数和析构函数

__init__特殊方法为构造函数,在创建对象时触发

__del__特殊方法为析构函数,在对象销毁时触发。通常如果不使用del关键字主动销毁对象,在程序结束时对象才会被销毁。

class Worker:
    def __init__(self):  # __init__ 初始化的时候调用
        print("构造了", id(self))
        
    def __del__(self):  # __del__ 删除的时候调用
        print("删除了", id(self))


w1 = Worker()
w2 = Worker()

'''
构造了 1655680304072
构造了 1655680304968
删除了 1655680304072
删除了 1655680304968
'''

3.有名对象和匿名对象

# 有名对象
c1 = Circle(2) 

# 匿名对象 
Circle(2) 

匿名对象的生命周期很短,在创建时进行初始化操作后立马就消失了。

而有名对象只要不主动进行删除,就一直存在在内存中,直到程序结束。

4.不同对象的属性和方法的地址

结论:
  • 同一个类的不同对象的地址不同。
  • 同一个类的不同对象的方法的地址相同。方法的地址相同说明方法是共享的,用 self 来区别是谁调用的
  • 同一个类的不同对象的属性的地址,在属性相等时是相同的,在属性不等时是不同的
import math


class Circle:
    def __init__(self, radius=1):
        self.radius = radius

    def get_area(self):
        return math.pi * (self.radius ** 2)


c1 = Circle(1)
c2 = Circle(1)
c3 = Circle(2)

print(id(c1) == id(c2))  # False
print(id(c2) == id(c3))  # False
print(id(c3) == id(c1))  # False

print(id(c1.radius) == id(c2.radius))  # True
print(id(c2.radius) == id(c3.radius))  # False
print(id(c3.radius) == id(c1.radius))  # False

print(id(c1.get_area) == id(c2.get_area))  # True
print(id(c2.get_area) == id(c3.get_area))  # True
print(id(c3.get_area) == id(c1.get_area))  # True

5.对象的引用与拷贝

对象赋值是引用

class Complex:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def show(self):
        print(self.x, "+", self.y, "i")


c1 = Complex(1, 2)
c2 = c1  # 引用,好处是节约内存
c3 = Complex(c1.x, c1.y)  
# 新创建一个对象,成员属性和原来一样,但两对象地址不同,这是拷贝,保证数据的安全

print(id(c1) == id(c2))   # True
print(id(c1) == id(c3))   # False
print(id(c2) == id(c3))   # False

c1.x = 100
c1.show()  # 100 + 2 i
c2.show()  # 100 + 2 i
c3.show()  # 1 + 2 i

由于对象赋值是引用,因此如果将对象作为函数实参传递给形参,如果函数内部对形参进行了值修改,则实参的值也会被修改。例子如下:

import math


class Circle:
    def __init__(self, radius=1):
        self.radius = radius

    def get_area(self):
        return math.pi * (self.radius ** 2)


def print_radius_area(c, times):
    print("radius\t\tarea")
    while times >= 1:
        print(c.radius, "\t\t", round(c.get_area(), 2))
        c.radius += 1
        times -= 1


def main():
    c1 = Circle()
    n = 5

    print("\nradius is", c1.radius)
    print("n is ", n, "\n")

    print_radius_area(c1, n)

    print("\nradius is", c1.radius)
    print("n is ", n, "\n")


main()

"""
radius is 1
n is  5 

radius		area
1 		 3.14
2 		 12.57
3 		 28.27
4 		 50.27
5 		 78.54

radius is 6
n is  5 
"""
可以发现 n 始终没变, n 为不可变对象,始终为 5。
而对象 c1的属性 radius 发生了变化,圆是可变对象参数,Circle 对象 c1 的属性 radius 在变化,由开始的 1 到最后的 6。

用拷贝保证数据安全

import math


class Circle:
    def __init__(self, radius=1):
        self.radius = radius

    def get_area(self):
        return math.pi * (self.radius ** 2)


def print_radius_area(c, times):
    # print_radius_area(c1, n)函数调用时,c1赋值给c,此时c是对c1的引用
    c = Circle(c.radius)
    # c = Circle(c.radius),Circle(c.radius)创建了一个新对象,然后赋值在c
    # 此时c不再是对c1的引用,因此后序对c的成员的改变不会影响到c1
    print("radius\t\tarea")
    while times >= 1:
        print(c.radius, "\t\t", round(c.get_area(), 2))
        c.radius += 1
        times -= 1


def main():
    c1 = Circle()
    n = 5

    print("\nradius is", c1.radius)
    print("n is ", n, "\n")

    print_radius_area(c1, n)

    print("\nradius is", c1.radius)
    print("n is ", n, "\n")


main()

6.数据私有化

私有化的重要性:
  • 通过数据域私有来保护数据,让类更容易维护,因为不私有时,数据可能被自己无意修改掉而不知,或者被别人篡改
  • 在不私有的情况下,可以在外部直接通过 r = c.radius 访问圆的数据域,可以在外部通过 c.radius = 2 来修改圆的数据域
  • 在私有的情况下,只能在类内部修改,或者通过特定的方法从外部访问
  • 在特定的方法那里加入一些权限,比如要输入密码等,就可以很好地保护数据(比如保护银行余额等)
对属性进行私有化的语法:添加下划线_两个
self.__radius = radius
数据私有化后,访问和设置的方法
def get_radius(self):
    return self.__radius

def set_radius(self, radius):
    self.__radius = radius
对方法进行私有化也是在方法名前面加两根下划线__
def __get_area(self):
    return math.pi * (self.radius ** 2)
实例研究
class BankCard:
    def __init__(self):
        self.__money = 100

    def 存款(self, num):
        self.__money += num

    def 取款(self, num, password):
        if password == "123456":
            self.__money -= num
        else:
            print("密码错误请重新输入")

    def 查询(self, password):
        if password == "123456":
            print(self.__money)
        else:
            print("密码错误请重新输入")


李鑫的钱钱 = BankCard()
李鑫的钱钱.查询("123456")  # 100

李鑫的钱钱.存款(100000)
李鑫的钱钱.查询("123456")  # 100100

李鑫的钱钱.取款(50000, "123456")  # 私有设计增加权限
李鑫的钱钱.查询("123456")  # 50100

李鑫的钱钱.取款(50000, "456")  # 密码错误请重新输入


# 外部访问私有成员将会出错
# print(李鑫的钱钱.__money)
# AttributeError: 'BankCard' object has no attribute '__money'

# 外部修改私有成员将不会成功
李鑫的钱钱.__money = -100000000  # 安全权限设计
print(李鑫的钱钱.__money)  # -100000000

李鑫的钱钱.查询("123456")  # 50100

7.动态增加方法

在对象创建好后再给对象增加方法

作用之一:迭代开发
import math


class Circle:
    def __init__(self, radius=1):
        self.radius = radius
        self.area = 0

    def get_perimeter(self):
        return 2 * self.radius * math.pi


c1 = Circle()
c2 = Circle(2)
# 此时c1只有两个方法get_perimeter和set_radius


# 现在我们对其增加一个方法
c1.print_info = lambda user: print(user, " get c1 info: radius =", c1.radius)
c1.print_info("tom")  # tom  get c1 info: radius = 1


# 再增加一个方法
def __get_area(m_c: Circle):
    m_c.area = math.pi * m_c.radius**2


c1.get_area = lambda: __get_area(c1)  # 匿名函数不传递参数

c1.get_area()
print(c1.area)  # 3.141592653589793

8.运算符重载

如果我们直接用自己定义的类创建的一些对象来进行加减乘除等运算符的使用,它会报错,因为我们没有对这些运算做定义。为运算符定义的方法被称作运算符重载。
# 为对象定义加法
class Complex:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def show(self):
        print(self.x, "+", self.y, "j")
    
    def __add__(self, other):
        if type(other) == type(self):
            _x = self.x + other.x
            _y = self.y + other.y
            return Complex(_x, _y)
        elif type(other) == int:
            return Complex(self.x + other, self.y)


c1 = Complex(1, 2)
c2 = Complex(3, 5)

c1.show()  # 1 + 2 j
c2.show()  # 3 + 5 j

c3 = c1 + c2
c3.show()  # 4 + 7 j

c4 = c1 + 5
c4.show()  # 6 + 2 j

运算符重载函数表

注意:你可以传递一个对象去调用print(x),这等价于调用print(x.__str__())或print( str(x) ),因此想调用print对自定义对象进行打印,需要重载__str__方法。

class Complex:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        if type(other) == type(self):
            _x = self.x + other.x
            _y = self.y + other.y
            return Complex(_x, _y)
        elif type(other) == int:
            return Complex(self.x + other, self.y)

    def __str__(self):
        ret = f"complex: ({self.x}+{self.y}i)"
        return ret


c1 = Complex(1, 2)
c2 = Complex(3, 5)

print(c1)  # complex: (1+2i)
print(c2)  # complex: (3+5i)

c3 = c1 + c2
print(c3)  # complex: (4+7i)

c4 = c1 + 5
print(c4)  # complex: (6+2i)

9.继承

单继承

从现有类定义新类,这被称为继承 。
一般是先定义一个通用类(父类),随后将它扩展为更多特定的类(子类)
子类会继承父类中的属性和方。
同名属性或者方法,子类覆盖父类。

例子1:

class 王健林:
    def __init__(self):
        self.money = 100000000
        self.mystr = "定个小目标,赚它一个亿"

    def doing(self):
        print("疯狂工作")

    def buy(self):
        print("有钱花")


class 王思聪(王健林):
    def _init__(self):
        super().__init__()  # 对所有父类对象初始化
        # 等价于: 王健林.__init__(self) # 但前者所全部父类对象初始化
        self.mystr = "我交朋友不看钱,因为谁都没有我有钱"

    def doing(self):
        print("我是国民老公,想泡谁就泡谁")

dawang = 王健林()
dawang.doing()     # 疯狂工作
print("王健林 money", dawang.money)  # 王健林 money 100000000
print(dawang.mystr)  # 定个小目标,赚它一个亿

print("-------------")
xiaowang = 王思聪()
xiaowang.doing()  # 我是国民老公,想泡谁就泡谁
print("王思聪 money", xiaowang.money)  # 王思聪 money 100000000
xiaowang.buy()  # 有钱花
print(xiaowang.mystr)  # 定个小目标,赚它一个亿

例子2:父类初始化:带上参数

class Parent:
    def __init__(self, work_time, money):
        self.work_time = work_time
        self.money = money


class Child(Parent):
    def __init__(self, work_time, money, play_time):
        super().__init__(work_time, money)
        self.play_time = play_time


oneChild = Child(20, 200, 30)
print(oneChild.money)  # 200

多重继承

# 多个父类
class Parent1:
    def __init__(self):
        self.money = 100
    
    def doing(self):
        print(f"money={self.money}, 正在赚钱")


class Parent2:
    def __init__(self):
        self.money = 1000000
        
    def doing(self):
        print(f"money={self.money}, 正在玩耍")


# 多个父类时,若父类们有相同的属性名或者方法名,继承最先出现的父类
class Child(Parent2, Parent1): 
    def __init__(self):
        super().__init__()

    def buy(self):
        self.money = 0


boy = Child()
print(boy.money)  # 1000000
boy.doing()  # money=1000000, 正在玩耍

boy.buy()
print(boy.money)  # 0

私有属性和方法不可继承

class Parent:
    def __init__(self):
        self.money = 100
        self.__wife = "刘亦菲"
    
    def __doing(self):
        print(f"money={self.money}, 正在赚钱")


class Child(Parent):
    def __init__(self):
        super().__init__()

    def buy(self):
        self.money = 0


boy = Child()
# print(boy.__wife)  # AttributeError: 'Child' object has no attribute '__wife'
# boy.__doing()  #  AttributeError: 'Child' object has no attribute '__doing'

object

python 中所有的类都是 object 类的子类。
如果一个类定义时没有指定它的父类,那么它的父类默认是 object 类。
class son1:
    pass


class son2(object):
    pass


object1 = son1()
print(dir(object1))  # dir 函数查看对象的所有属性和方法

object2 = son2()
print(dir(object2))

print(dir(object))

多态(动态绑定)

类型兼容原则:需要传递父类的参数,那么其子类也可以被传递。

class Sever:
    def make(self):
        print("生产食物")


class MakeApple(Sever):
    def make(self):
        Sever.__init__(self)
        print("生产苹果汽水")


def make_product(server_product: Sever):
    server_product.make()


print(isinstance(MakeApple(), Sever))  # True

m = Sever()
m1 = MakeApple()
make_product(m)  # 生产食物
make_product(m1)  # 生产苹果汽水

多态(动态绑定)含义:一个方法可以沿着继承链被很多子类继承,运行时具体调用哪个子类的该方法,由 python 动态决定。动态绑定的机制如下:

class People:
    def doing(self):
        print("freedom")


class GrandParent(People):
    def doing(self):
        print("live")


class Parent(GrandParent):
    def doing(self):
        print("work")


class Child(Parent):
    pass


obj = Child()
obj.doing()  # work

10.isinstance 函数

isinstance 函数用来判断一个对象是否是一个类的实例
x = 10
mystr = "好好学习"

print(isinstance(x, int))  # True
print(isinstance(x, float))  # False
print(isinstance(mystr, str))  # True


class Father:
    pass


class Son(Father):
    pass


print("-----")
print(isinstance(Father(), Father))  # True
print(isinstance(Son(), Father))  # True # 子类属于父类
print(type(Father()))  # <class '__main__.Father'>
print(type(Father))    # <class 'type'>
print(type(Father()) == type(Father))  # False
print(type(Son()) == type(Father))  # False # type 对类型进行严格检查

11.静态方法

类的方法分为两种:
  • 普通方法:对于类的普通方法,不同对象的该方法地址不同,不是同一个方法
  • 静态方法:对于类的静态方法,不同对象的共享该方法,同一个地址
class People:
    def __init__(self, tall):
        self.tall = tall

    @staticmethod  # 静态方法
    def get_eyes():
        return 2

    # @classmethod  # 标识符,可以省略,类的内部方法
    def get_tall(self):
        return self.tall


print(People.get_eyes())  # 通用的东西,静态方法
# print(People.get_tall()) # 这个就不行了,因为get_tall不是静态方法


p1 = People(175)
print(p1.get_eyes())  # 实例化的对象也可以调用静态方法
print(p1.get_tall())  # 实例化的对象也可以调用

end

  • 26
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值