1 基本使用
关于类的与对象的基本使用
class Student:
# 类属性
class_count = 0
def __init__(self, name, age): # 构造方法 创建对象的时候用于初始化对象 __init__是固定写法
self.name = name
self.age = age
Student.class_count += 1
# 实例方法
def get_name(self):
return self.name
# 静态方法
@staticmethod
def add(a, b):
return a + b
# 类方法
@classmethod
def get_class_count(cls):
return cls.class_count
s = Student("张三", 20)
print(s.get_name()) # 实例方法的调用
s2 = Student("李四", 20)
print("共创建{}个Student类".format(Student.class_count,)) # 类属性的使用
print(Student.add(1, 2)) # 静态方法的使用
print(Student.get_class_count()) # ;类方法的使用
类方法使用 @classmethod
装饰器来定义。第一个参数必须是类本身,通常命名为 cls
。可以通过类名或者类的实例来调用。类方法主要用于对类本身进行操作,例如修改类属性、创建类的实例等。
class MyClass:
class_attribute = "class value"
@classmethod
def class_method(cls):
print(f"Class method called. class_attribute: {cls.class_attribute}")
# 调用方式
MyClass.class_method() # 通过类名调用
instance = MyClass()
instance.class_method() # 通过实例调用
静态方法使用 @staticmethod
装饰器来定义。它不需要任何特殊的第一个参数(既不是类本身也不是实例)。可以通过类名或者类的实例来调用。静态方法通常用于定义与类相关但不需要访问类属性或实例属性的方法。它们更类似于普通的函数,只不过是属于类的命名空间。
class MyClass:
class_attribute = "class value"
@staticmethod
def static_method():
print("Static method called.")
# 调用方式
MyClass.static_method() # 通过类名调用
instance = MyClass()
instance.static_method() # 通过实例调用
类方法和静态方法的区别
- 第一个参数:
- 类方法:第一个参数是类本身,通常命名为
cls
。 - 静态方法:没有特定的第一个参数。
- 类方法:第一个参数是类本身,通常命名为
- 用途:
- 类方法:用于操作类本身或访问/修改类属性。
- 静态方法:用于独立于类或实例的通用功能,不需要访问类属性或实例属性。
- 装饰器:
- 类方法:使用
@classmethod
装饰器。 - 静态方法:使用
@staticmethod
装饰器。
- 类方法:使用
- 访问权限:
- 类方法:可以访问类属性和类方法。
- 静态方法:不能访问类属性或实例属性,只能访问它自己的参数。
2 内置方法
内置方法 | 说明 |
---|---|
__init__(self, ) | 初始化对象,在创建新对象时调用 |
__del__(self,) | 释放对象,在对象被删除的时候调用 |
__new__(cls, *args, **kwd) | 实例的生成对象 |
__str__(self) | 在使用print语句打印对象的时候调用 |
__getitem__(self, key) | 获取序列的索引key对应的值,等价于sea[key] |
__len__(self) | 在调用内联函数len()时被调用 |
__cmp__(src, dst) | 比较两个对象src和dst |
… | … |
class Student:
# 类属性
class_count = 0
def __init__(self, name, age):
self.name = name
self.age = age
Student.class_count += 1
def __del__(self,):
print("对象被销毁了")
def __str__(self):
return "调用print方法" + self.name
s1 = Student("maple", 18)
print(s1) # 调用print方法maple
del s1 # 对象被销毁了
class SalaryAccount:
'''工资计算类'''
def __call__(self, salary):
yearSalary = salary*12
daySalary = salary//30
hourSalary = daySalary//8
return dict(monthSalary=salary,yearSalary=yearSalary,daySalary=daySalary,hourSalary=hourSalary)
s = SalaryAccount()
print(s(12000))
3 运算符重载
运算符的重载 主要是存在 对象需要进行加减乘除操作的场景
+ -> __add__
- -> __sub__
<, <=, == -> __lt__, __le__, __eq__
>, >=, != -> __gt__, __ge__, __ne__
|, ^, & -> __or__, __xor__, __and__
<<, >> -> __lshift__, __rshift__
*, /, %, // -> __mul__, __truediv__, __mode__, floordiv__
** -> __pow__
示例
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __add__(self, otherObj):
if isinstance(otherObj, Student):
return self.age + otherObj.age
else:
return "不是同一类对象,不能相加"
s1 = Student("m1", 18)
s2 = Student("m2", 20)
print(s1 + s2) # 38
print(s1 + 10) # 不是同一类对象,不能相加
4 私有属性与私有方法
python对于类的成员没有严格的访问控制限制,这与其他面向对象语言有区别。关于私有属性和私有方法,有如下要点:
- 通常约定,两个下划线开头的属性是私有的,其他为公共的
- 类内部可以访问私有属性(方法)
- 类外部不能直接访问私有属性(方法)
- 类外部可以通过
_类名__私有属性(方法)名
访问私有属性(方法)
class Student:
def __init__(self, name, age):
self.name = name
self.__age = age # 私有属性
def selfIntroduce(self):
self.__sayHello()
print("my name is {}, my age is {}".format(self.name, self.__age))
def __sayHello(self): # 私有方法
print("hello")
s1 = Student("maple", 22)
print(s1.name)
s1.selfIntroduce()
print(s1._Student__age) # 通过命名规范访问私有属性
print(s1.__age) # 无法直接访问私有属性
print(s1.__sayHello()) # 无法直接访问私有方法
5 关于方法重载
python中,方法的参数没有声明类型,参数的数量也可以由可变参数控制。因此,python中没有方法重载。定义一个方法即可以有多种调用方法,相当于实现了其他语言中的方法重载
如果在类中定义了多个重名的方法,只有最后一个方法有效
6 type与isinstance函数
class Student:
def __init__(self, name, age):
self.name = name
self.__age = age # 私有属性
s1 = Student("maple", 22)
print(type(s1)) # type 判断对象类型
print(isinstance(s1, Student)) # istanceof 判断类型
print(type("1"))
7 @property装饰器
在绑定属性的时候,直接把属性暴露出去,导致可以随便修改,如
class Person:
pass
s = Person()
s.name = "maple"
通常我们会设置一个get和set方法,如
class Person():
def __init__(self, name, score):
self.name = name
self.__score = score
def get_score(self):
return self.__score
def set_score(self, value):
if not isinstance(value, int):
print("成绩必须是整数")
else:
if value < 0 or value > 10:
print("成绩必须在0-100之间")
else:
self.__score = value
s = Person("maple", 60)
print("成绩是", s.get_score()) # 成绩是 60
s.set_score(6)
print("成绩是", s.get_score()) # 成绩是 6
s.set_score(1000) # 成绩必须在0-100之间
s.set_score("aaa") # 成绩必须是整数
但是这种方法会显得比较复杂,因此我们引入 @property
装饰器
class Person():
def __init__(self, name, score):
self.name = name
self.__score = score
@property
def score(self):
return self.__score
@score.setter
def score(self, value):
if not isinstance(value, int):
print("成绩必须是整数")
else:
if value < 0 or value > 10:
print("成绩必须在0-100之间")
else:
self.__score = value
s = Person("maple", 60)
print("成绩是", s.score) # 成绩是 60
s.score = 6
print("成绩是", s.score) # 成绩是 6
s.score = 1000 # 成绩必须在0-100之间
s.score = "aaa" # 成绩必须是整数
设置只读属性
import datetime
class Student(object):
@property
def birth(self):
return self.__birth
@birth.setter
def birth(self, value):
self.__birth = value
@property
def age(self):
#获取当前年份
now_year=datetime.datetime.now().strftime('%Y')
return int(now_year) - int(self.__birth)
s=Student()
s.birth='1998' #设置出生年份
print('出生日期:',s.birth) #获取出生年份
print('年龄:',s.age) #获取年龄
8 面向对象三大特征
- 封装:隐藏对象的属性和实现细节,只对外提供必要的方法。前面私有属性、私有方法就是封装的体现。Python没有严格的语法级别的访问控制符,是通过程序员的编码规范自觉实现
- 继承:子类没具有父类的特性,提升代码的重用性
- 多态:同一个方法调用由于对象不同产生不同的行为,如人吃菜,张三吃猪肉,李四吃牛肉。
8.1 继承
类的继承,就是一个类(子类)从另外一个类(父类)获得了所有的成员。父类的成员可以在子类中使用。
class Person:
def sing(self):
print('人在唱歌')
class Student(Person): # 继承person类
def dance(self):
print('学生在跳舞')
s1 = Student()
s1.sing() #调用父类的方法
s1.dance()
子类继承了父类除构造方法之外的所有成员,如果父类中方法的功能不能满足子类的需求,子类可以重写父类的方法。示例如下:
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def say_age(self):
print("年龄:",self.age)
def say_name(self):
print("姓名:",self.name)
class Student(Person):
def __init__(self,name,age,score):
self.score = score
Person.__init__(self,name,age) #构造函数中包含调用父类构造函数
super().__init__(name,age) # 也可以通过 super()函数来获取父类方法
def say_score(self):
print("分数是:",self.score)
#重写父类的方法
def say_name(self):
print("报告老师,我是",self.name)
s1 = Student("张三",15,85)
s1.say_score()
s1.say_name()
s1.say_age()
检查继承关系
在很多场景中,需要知道一个类 A 是否是从另外一个类 B 继承,这种校验主要是为了调用 B 类中的成员(方法和属性)。如果 B 是 A 的父类,那么创建 A 类的实例肯定会拥有 B类所有的成员,关键是要判断 B 是否是 A 的父类可以使用 issubclass 函数。
class Person(object):
pass
class Child(Person): # Child 继承 Person
pass
class GrandSon(Child):
pass
print(issubclass(Child,Person)) # True
print(issubclass(GrandSon,Person)) # True
多继承
#多重继承
class A:
def aa(self):
print("aa")
class B:
def bb(self):
print("bb")
class C(B,A):
def cc(self):
print("cc")
8.2 多态
多态指的是同一个方法调用,由于对象不同可能会有不同的行为。现实生活中,同一个方法,具体实现会完全不同。 比如:同样是调用人的“休息”方法,张三是睡觉,李四是旅游; 同样是调用人“吃饭”的方法,中国人用筷子吃饭,英国人用刀叉吃饭,印度人用手吃饭。
- 多态是方法的多态,不是属性的多态(多态与属性无关)。
- 多态的存的必要条件:继承,方法重写。
#多态
class Animal:
def shout(self):
print("动物叫了一声")
class Dog(Animal):
def shout(self):
print("小狗,汪汪汪")
class Cat(Animal):
def shout(self):
print("小猫,喵喵喵")
def animalShout(a):
if isinstance(a,Animal):
a.shout() #传入的对象不同,shout 方法对应的实际行为也不同。
animalShout(Dog())
animalShout(Cat())
8.3 对象拷贝问题
深拷贝和浅拷贝:浅拷贝指不拷贝子对象的内容,只是拷贝子对象的引用。深拷贝连子对象的内容也拷贝一份,对子对象的修改不影响原对象
copy()
、 deepcopy()
分别来实现浅拷贝、深拷贝
import copy
def testCopy():
'''测试浅拷贝'''
a = [10, 20, [5, 6]]
b = copy.copy(a)
print(id(a) == id(b)) # false
b.append(30)
b[2].append(7)
print("浅拷贝......")
print("a", a) # [10, 20, [5, 6]]
print("b", b) # [10, 20, [5, 6, 7], 30]
a = [10, 20, [5, 6]]
b = copy.deepcopy(a)
print(id(a) == id(b)) # false
b.append(30)
b[2].append(7)
print("深拷贝......")
print("a", a) # [10, 20, [5, 6]]
print("b", b) # [10, 20, [5, 6, 7], 30]
testCopy()
Python 拷贝一般都是浅拷贝。拷贝时,对象包含的子对象内容不拷贝。因此,源对象和拷贝对象会引用同一个子对象。如果想实现深拷贝可以使用 copy 模块的 deepcopy 函数,递归拷贝对象中包含的子对象。源对象和拷贝对象所有的子对象也不同。
#测试对象的引用赋值、浅拷贝、深拷贝
import copy
class MobilePhone:
def __init__(self,cpu,screen):
self.cpu = cpu
self.screen = screen
class CPU:
def calculate(self):
print("CPU 对象:",self)
class Screen:
def show(self):
print("屏幕对象:",self)
c = CPU()
s = Screen()
m = MobilePhone(c,s)
print('------浅拷贝-------') # ------浅拷贝-------
m2 = copy.copy(m) #m2 是新拷贝的另一个手机对象
m.cpu.calculate() # CPU 对象: <__main__.CPU object at 0x1050303d0>
m2.cpu.calculate() #m2 和 m 拥有了一样的 cpu 对象和 screen 对象 CPU 对象: <__main__.CPU object at 0x1050303d0>
m.screen.show() # 屏幕对象: <__main__.Screen object at 0x105030390>
m2.screen.show() # 屏幕对象: <__main__.Screen object at 0x105030390>
print('------深拷贝-------')
m3 = copy.deepcopy(m)
m.cpu.calculate() # CPU 对象: <__main__.CPU object at 0x1050303d0>
m3.cpu.calculate() #m3 和 m 拥有不一样的 cpu 对象和 screen 对象 CPU 对象: <__main__.CPU object at 0x102d04710>
m.screen.show() # 屏幕对象: <__main__.Screen object at 0x102cf43d0>
m3.screen.show() # 屏幕对象: <__main__.Screen object at 0x102d04790>