类也是一个对象!
类就是一个用来创建对象的对象!
类是type类型的对象,定义类实际上就是定义了一个type类型的对象
嗯,神奇Python里是万物皆对象,玄学就完事了。。。
下图是对象创建的流程
在类的代码块中,我们可以定义变量和函数。在类中我们所定义的变量,将会成为所有的实例的公共属性,所有实例都可以访问这些变量。
注:为什么类的方法第一个形参是self(当然也可以是别的名字),这是为了指定是哪个类调用的,比如下方代码中self.name也可是换成p1.name,但是这样不具有通用性,因为每次调用的都是p1的属性值。方法每次被调用时,解析器都会自动传递第一个实参,第一个参数,就是调用方法的对象本身。
class Person :
name = 'swk' # 公共属性,所有实例都可以访问
def say_hello(self) :
# 方法每次被调用时,解析器都会自动传递第一个实参
# 第一个参数,就是调用方法的对象本身,
# 如果是p1调的,则第一个参数就是p1对象
# 如果是p2调的,则第一个参数就是p2对象
# 一般我们都会将这个参数命名为self
# 在方法中不能直接访问类中的属性
print('你好!我是 %s' %self.name)
# 创建Person的实例
p1 = Person()
p2 = Person()
print(p2.name)
# 修改p1的name属性
p1.name = '猪八戒'
p2.name = '沙和尚'
p1.say_hello() # '你好!我是 猪八戒'
p2.say_hello() # '你好!我是 沙和尚'
__init__()方法
创建对象的流程:
p1 = Person()的运行流程
1.创建一个变量
2.在内存中创建一个新对象
3.__init__(self)方法执行
4.将对象的id赋值给变量
init会在对象创建以后离开执行
init可以用来向新创建的对象中初始化属性
调用类创建对象时,类后边的所有参数都会依次传递到init()中
class Person :
def __init__(self,name):
self.name = name
def say_hello(self):
print('大家好,我是%s'%self.name)
p1 = Person('孙悟空')
p2 = Person('猪八戒')
p3 = Person('沙和尚')
p4 = Person('唐僧')
对象属性或方法的隐藏(伪隐藏)
可以为对象的属性使用双下划线开头,__xxx
其实隐藏属性只不过是Python自动为属性改了一个名字
实际上是将名字修改为了,_类名__属性名 比如 __name -> _Person__name
class Person:
def __init__(self,name):
self.__name = name
def get_name(self):
return self.__name
def set_name(self , name):
self.__name = name
p = Person('孙悟空')
print(p.__name) __开头的属性是隐藏属性,无法通过对象访问
p.__name = '猪八戒'
print(p._Person__name)
p._Person__name = '猪八戒'
print(p.get_name())
使用__开头的属性,实际上依然可以在外部访问,所以这种方式我们一般不用,一般我们会将一些私有属性(不希望被外部访问的属性)以_开头,我就认为使用_开头的属性都是私有属性,没有特殊需要不要修改私有属性。当然也可以直接修改。。。
property装饰器:
用来将一个get方法,转换为对象的属性
添加为property装饰器以后,我们就可以像调用属性一样使用get方法
使用property装饰的方法,必须和属性名是一样的
setter方法的装饰器:@属性名.setter
class Person:
def __init__(self,name,age):
self._name = name
self._age = age
@property
def name(self):
print('get方法执行了~~~')
return self._name
@name.setter
def name(self , name):
print('setter方法调用了')
self._name = name
@property
def age(self):
return self._age
@age.setter
def age(self , age):
self._age = age
p = Person('猪八戒',18)
p.name = '孙悟空'
p.age = 28
print(p.name,p.age)
super()
def __init__(self,name,age):
# 希望可以直接调用父类的__init__来初始化父类中定义的属性
# super() 可以用来获取当前类的父类,
# 并且通过super()返回对象调用父类方法时,不需要传递self
super().__init__(name)
self._age = age
面向对象的三大特征:
封装
- 确保对象中的数据安全
继承
- 保证了对象的可扩展性
多态
- 保证了程序的灵活性
上边两个和Java差不多,下面只是讲解一下啊多态
class A:
def __init__(self,name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class B:
def __init__(self,name):
self._name = name
def __len__(self):
return 10
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class C:
pass
a = A('孙悟空')
b = B('猪八戒')
c = C()
# 定义一个函数
# 对于say_hello()这个函数来说,只要对象中含有name属性,它就可以作为参数传递
# 这个函数并不会考虑对象的类型,只要有name属性即可
def say_hello(obj):
print('你好 %s'%obj.name)
# 在say_hello_2中我们做了一个类型检查,也就是只有obj是A类型的对象时,才可以正常使用,
# 其他类型的对象都无法使用该函数,hello_2()这个函数就违反了多态
# 违反了多态的函数,只适用于一种类型的对象,无法处理其他类型对象,这样导致函数的适应性非常的差
# 注意,向isinstance()这种函数,在开发中一般是不会使用的!
def say_hello_2(obj):
# 做类型检查
if isinstance(obj , A):
print('你好 %s'%obj.name)
多态的应用:
比如 len() 内建函数
之所以一个对象能通过len()来获取长度,是因为对象中具有一个特殊方法__len__,换句话说,只要对象中具有__len__特殊方法,就可以通过len()来获取它的长度。相当于是在len()函数内,会获取__len__方法的返回值。比如上方代码的class B