> For the complete documentation index, see [llms.txt](https://zeliang-yao.gitbook.io/my-note-zeliang-yao/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://zeliang-yao.gitbook.io/my-note-zeliang-yao/useful/python-oop/basic.md).

# Basic

### Concepction

| 概念           | 解释                                                                                                                                      |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| **类(Class)** | 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例                                                                                      |
| **类变量**      | 类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用                                                                                        |
| **数据成员**     | 类变量或者实例变量, 用于处理类及其实例对象的相关的数据                                                                                                            |
| **方法重写**     | 如果从父类继承的方法不能满足子类的需求，可以对其进行改写，这个过程叫方法的覆盖（override），也称为方法的重写                                                                              |
| **局部变量**     | 定义在方法中的变量，只作用于当前实例的类                                                                                                                    |
| **实例变量**     | 在类的声明中，属性是用变量来表示的。这种变量就称为实例变量，是在类声明的内部但是在类的其他成员方法之外声明的                                                                                  |
| **继承**       | 即一个派生类（derived class）继承基类（base class）的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如，有这样一个设计：一个Dog类型的对象派生自Animal类，这是模拟"是一个（is-a）"关系（例图，Dog是一个Animal） |
| **实例化**      | 创建一个类的实例，类的具体对象                                                                                                                         |
| **方法**       | 类中定义的函数                                                                                                                                 |
| **对象**       | 通过类定义的数据结构实例。对象包括两个数据成员（类变量和实例变量）和方法                                                                                                    |

#### 定义一个类

```python
class Car:
    def __init__(self, color, model, year):

        self.color = color
        self.model = model
        self.year = year
```

### Create object

```
my_car = Car("yellow", "beetle", 1967)
```

查看一下my\_car的属性

```python
print(f" My {my_car.color} car {my_car.model} is made in {my_car.year}")
# My yellow car beetle is made in 1967
```

### Add attribute

```python
my_car.wheels = 5
print(f"Wheels: {my_car.wheels}")
# Wheels: 5
```

```python
dir(my_car)

Out:
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'color',
 'model',
 'wheels',     <====已经添加成功啦
 'year']
```

### Class variable

在Python中，我们在类外声明一个类变量，下面让我们修改一下Car类：

```python
class Car:
    wheels = 0
    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year
```

这样的话，我们在调用wheels这个变量时，可以通过实例，或者直接调用Car.wheels：

```python
my_car = Car("yellow", "beetle", 1967)
print(f"My car is {my_car.color}")
print(f"It has {Car.wheels} wheels")
print(f"It has {my_car.wheels} wheels")

Out:
My car is yellow
It has 0 wheels
It has 0 wheels
```

这里需要注意一下，如果想要通过my\_car.wheels =xxx来修改wheels的值，不会真正修改类变量wheels的值，我们来看一个具体的例子：

```python
my_car = Car("yellow", "Beetle", "1966")
my_other_car = Car("red", "corvette", "1999")

print(f"My car is {my_car.color}")
print(f"It has {my_car.wheels} wheels")


print(f"My other car is {my_other_car.color}")
print(f"It has {my_other_car.wheels} wheels")

Out：
My car is yellow
It has 0 wheels
My other car is red
It has 0 wheels
```

我们首先创建两个实例my\_car 和my\_other\_car ，默认的wheels=0，下面我们首先直接通过Car这个类来修改类变量的值：

```python
# Change the class variable value

Car.wheels = 4

print(f"My car has {my_car.wheels} wheels")
print(f"My other car has {my_other_car.wheels} wheels")

Out：
My car has 4 wheels
My other car has 4 wheels
```

可以看到这样修改的话，Car类拥有的所有实例中的wheels值会被全部修改，如果我们通过my\_other\_car 来修改呢？

```python
# Change the instance variable value for my_car

my_car.wheels = 5
print(f"My car has {my_car.wheels} wheels")
print(f"My other car has {my_other_car.wheels} wheels")

Out:
My car has 5 wheels
My other car has 4 wheels
```

现在大家可以发现区别了，仅仅是修改了my\_car中wheels的值，对类本身不会造成影响在Python中的所有属性都是public，可能有c++和java的同学觉得神奇，其实python最初规定了一种特殊的命名方式来区分public还是private，那就是下划线\_

### Private & Public attribute

```python
class Car:
    wheels = 0
    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year
        self._cupholders = 6

my_car = Car("yellow", "Beetle", "1969")
print(f"It was built in {my_car.year}")

Out：
It was built in 1969
```

这里Car类中的杯托 \_cupholders就是“私有“属性，为什么我这里加上了引号，是因为Python只是名义上规定这种写法，但是在实际访问上没啥卵用，依然可以直接用.\_cupholders来访问：

```python
my_car.year = 1966
print(f"It was built in {my_car.year}")
print(f"It has {my_car._cupholders} cupholders.")

Out:
It was built in 1966
It has 6 cupholders.
```

后来Python决定使用双下划线\_\_来替换单下划线，这样可以最大程度避免“意外访问“，然而还是没有卵用，再来展示一下新方案：

```python
class Car:
    wheels = 0
    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year
        self.__cupholders = 6
```

其实某种程度上，这回效果还是很明显的，如果我们还像刚才一样尝试调用my\_car.cupholders 会报错：

```python
my_car = Car("yellow", "Beetle", "1969")
print(f"It was built in {my_car.year}")
print(f"It has {my_car.__cupholders} cupholders.")


Out:
It was built in 1969

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-108-1efe56f0c054> in <module>
 1 my_car = Car("yellow", "Beetle", "1969")
 2 print(f"It was built in {my_car.year}")
----> 3  print(f"It has {my_car.__cupholders} cupholders.")

AttributeError: 'Car' object has no attribute '__cupholders'
```

这个错误很有意思，为什么会说cupholders这个变量不存在呢 ? 因为当Python看到\_\_ 时，会自动在cupholders前面补上一个下划线\_和所属类名，也就是说，这里我们尝试用my\_car.\_\_cupholders 来调用时，Python默认的正确写法是 my\_car.\_Car\_\_cupholders，现在再试一下：

```
print(f"It has {my_car._Car__cupholders} cupholders")
Out: It has 6 cupholders
```

依然没拦住。。。。 不过我个人认为这种规定公有私有变量的方式也是好处多多，这里就仁者见仁，智者见智了\~

## Manage access&#x20;

就像刚刚提到的，Python所有的东西都是公有的，我们可以随意的新增，修改，甚至删除变量：

```python
my_car = Car("yellow", "beetle", 1969)
print(f"My car was built in {my_car.year}")

my_car.year = 2003
print(f"It was built in {my_car.year}")

del my_car.year
print(f"It was built in {my_car.year}")

Out：
My car was built in 1969
It was built in 2003

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-110-46914b0bae82> in <module>
 6 
 7 del my_car.year
----> 8  print(f"It was built in {my_car.year}")

AttributeError: 'Car' object has no attribute 'year'
```

那我们如何才能控制属性的访问权限呢？Python给出的答案是装饰器 @property，这个类似于Java中的setter和getter，现在我们试试：

```python
class Car:
    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year
        self._voltage = 12

    @property
    def voltage(self):
        return self._voltage

    @voltage.setter
    def voltage(self, volts):
        print("Warning: this can cause problems!")
        self._voltage = volts

    @voltage.deleter
    def voltage(self):
        print("Warning: the radio will stop working!")
        del self._voltage
```

我们新增了voltage（电压）这个属性，并用property来控制外部的访问权限，这里我们定义了三个方法，利用setter方法可以改变voltage的值，利用getter方法来访问，利用deleter方法实现删除，接下来让我们新建实例来看看propert是如何工作的：

```python
my_car = Car("yellow", "beetle", 1969)
print(f"My car uses {my_car.voltage} volts")

my_car.voltage = 6
print(f"My car now uses {my_car.voltage} volts")

del my_car.voltage

Out:
My car uses 12 volts
Warning: this can cause problems!
My car now uses 6 volts
Warning: the radio will stop working!
```

可以发现，我们这里直接使用.voltage 而不是.\_voltage，这样就告诉python去使用property装饰的方法，我们可以通过使用@.setter and @.deleter 使属性变为read-only（只读），从而保护voltage不会被随意修改和删除

### \_\_slots\_\_

```python
 class Celebrity:
     # 限定 Celebrity对象只能绑定name, age,domain属性,加速
    __slots__ = ['name','age',"domain"]
    # Class Attribute
    species = 'human'
    
    # Initializer / Instance Attributes
    def __init__(self, name, age, domain):
        self.name = name
        self.age = age
        self.domain = domain
```

这里注意我们用slots绑定了三个属性给Celebrity，这就是slots的作用：

* 如果我们需要限定自定义类型的对象只能绑定某些属性，可以通过在类中定义\_\_slots\_\_变量来进行限定。需要注意的是\_\_slots\_\_的限定只对当前类的对象生效，对子类并不起任何作用。
* 加速

现在可以做个实验，首先我们把slots绑定的domian属性去掉：

```python
class Celebrity:
    
    # Class Attribute
    species = 'human'
    __slots__ = ['name', 'age']
    # Initializer / Instance Attributes
    def __init__(self, name, age,domain):
        self.name = name
        self.age = age
        self.domain = domain
        
female_leader = Celebrity("Miss Dong",65,"electrical appliance")

# Access the instance attributes
print("{} is {}.".format(
    female_leader.name, female_leader.age))

Out:AttributeError: 'Celebrity' object has no attribute 'domain'
```

会发现报错了，即便我们在init方法中有domain属性，但是由于slots中没有，所以Celebrity类下创建的对象都不能有domain,接下来让我们简单回顾一下如何调用类变量：

```python
female_leader = Celebrity("Miss Dong", 65,"electrical appliance")
male_leader = Celebrity("Jack Ma", 55,"internet")

# Access the instance attributes
print("{} is {} and {} is {}.".format(
    female_leader.name, female_leader.age, male_leader.name, male_leader.age))

# Is male_leader a human?
if male_leader.species == "human":
    print("{0} is a {1}!".format(male_leader.name, male_leader.species))  

Out:
Miss Dong is 65 and Jack Ma is 55.
Jack Ma is a human!
```

### \*args

其实*args应该和*kargs一起来说，但是今天先重点看一下它在对象中的应用，我们现在给Celebrity类新建3个对象，并且我们想知道年龄最大的是谁 ？

这种情况下\*args很好用：

```python
a = Celebrity("Miss Dong",65,"electrical appliance")
b = Celebrity("Jack Ma", 55,"internet")
c = Celebrity("Lei Jun", 50,"mobile")

def get_oldest(*args):
    return max(args)

print("The big brother is {} years old.".format(get_oldest(a.age, b.age, c.age)))

Out:
The big brother is 65 years old.
```

### Inheritance

首先，我们在Celebrity类中新增两个方法:

* description:对生成的大佬简单描述
* speak: 大佬发言

完成后的结果如下：

```python
class Celebrity:
     

    __slots__ = ['name', 'age',"domain"]
    species = 'human'

    def __init__(self, name, age, domain):
        self.name = name
        self.age = age
        self.domain = domain
        
     # instance method
    def description(self):
        return "{} is {} years old, working in the {} industry".format(self.name, self.age,self.domain)

    # instance method
    def speak(self, sound):
        return "{} says {}".format(self.name, sound)
```

现在新建两个类InternetBoss，MobileBoss，全部继承于 Celebrity类：

```python
    # Child class (inherits from Dog() class)
    class InternetBoss(Celebrity):
        pass

    # Child class (inherits from Dog() class)
    class MobileBoss(Celebrity):
        pass
```

如果我们什么都不做，会自动继承父类的 description和speak方法，我们做个实验，新建li作为InternetBoss的对象：

```python
li = InternetBoss("Robbin",50,"advertisement")
```

调用description和speak方法：

```python
li.description()
li.speak("What's your problem ?")

Out:
Robbin is 50 years old, working in the advertisement industry
Robbin says: What's your problem ?

lei = MobileBoss("leijun", 50,"mobile")
lei.speak("Are you ok ?")

Out:
leijun says: Are you ok ?
```

### Polymorphisn & Override

```python
class InternetBoss(Celebrity):
    def description(self):
        print("I'm Internet Boss !")
         
class MobileBoss(Celebrity):
    def description(self):
        print("I'm Mobile phone Boss !")
            
            
li = InternetBoss("Robbin",50,"advertisement")
lei = MobileBoss("leijun", 50,"mobile")

li.description()
lei.description()

Out:
I'm Internet Boss !
I'm Mobile phone Boss !
```

#### isinstance() & issubclass()

Python 有两个判断继承的函数：

* isinstance() 用于检查实例类型
* issubclass() 用于检查类继承

```python
class Celebrity:
    __slots__ = ['name', 'age',"domain"]
    species = 'human'

    def __init__(self, name, age, domain):
        self.name = name
        self.age = age
        self.domain = domain
        
    def description(self):
        print( "{} is {} years old, working in the {} industry".format(self.name, self.age,self.domain))

    def speak(self, sound):
        print("{} says: {}".format(self.name, sound))
        
        
        
class InternetBoss(Celebrity):
    def description(self):
        print("I'm Internet Boss !")
         
class MobileBoss(Celebrity):
    def description(self):
        print("I'm Mobile phone Boss !")
        
mingzhu = Celebrity("Miss Dong",65,"electrical appliance")
ma= InternetBoss("Pony", 48,"internet")
lei = MobileBoss("leijun", 50,"mobile")    

# 现在使用issubclass（）判断InternetBoss和MobileBoss是否继承自Celebrity：

issubclass(InternetBoss,Celebrity) 
# True
issubclass(MobileBoss,Celebrity) 
# True

#使用isinstance（）查看mingzhu到底是谁的实例：
isinstance(mingzhu,Celebrity)
# True

isinstance(mingzhu,InternetBoss)
# False

isinstance(mingzhu,MobileBoss)
# False

#同理查看ma到底是哪个类的实例：
isinstance(ma,Celebrity)
# True

isinstance(ma,InternetBoss)
# True

isinstance(ma,MobileBoss)
# False

```

### Sub class overwrite  \_\_init\_\_

刚才提到了，如果子类没有写构造函数init（），会自动继承父类的init，但我们通常需要子类有不同的初始函数，这样我们就需要自己复写一下，这里以InternetBoss为例：

```python
class InternetBoss(Celebrity):
    def __init__(self,name, age, domain,hometown):
        super().__init__(name, age, domain)
        self.hometown = hometown
        
    
    def description(self):
        print("I'm Internet Boss !")
        
    def __repr__(self):
        return f"This is {self.name} speaking !"
```

### Example

```python
import random
class BingoCage:
    __slots__ = ("_items")
    def __init__(self, items):
        self._items = list(items) 
        random.shuffle(self._items) 

    def pick(self): 
        try:
            return self._items.pop()
        except IndexError:
            raise LookupError('pick from empty BingoCage') 
    def __call__(self): 
        return self.pick()
        
        

bingo= BingoCage(range(20))
bingo()  # 10
```

<br>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://zeliang-yao.gitbook.io/my-note-zeliang-yao/useful/python-oop/basic.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
