日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

python质数列_现代化程序开发笔记(3)——多文件与模块

發(fā)布時間:2024/9/19 python 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python质数列_现代化程序开发笔记(3)——多文件与模块 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本系列文章以我的個人博客的搭建為線索(GitHub 倉庫:Evian-Zhang/evian-blog),記錄我在現(xiàn)代化程序設(shè)計中的一些筆記。在這篇文章中,我將對現(xiàn)代編程語言的多文件和模塊部分進(jìn)行一些介紹。

模塊化編程

隨著現(xiàn)代編程開發(fā)項目的代碼量越來越大,參與開發(fā)維護(hù)的人數(shù)越來越多,模塊化編程這一理念變得十分重要。就像我所說的,模塊化編程實際上是一個理念,它倡導(dǎo)的是開發(fā)者利用各種手段,將不同作用的代碼塊隔離。比方說,眾所周知,巫師三的兩個核心功能是昆特牌功能和與女術(shù)士增進(jìn)感情的功能。假設(shè)我們是簡陋版巫師三的開發(fā)者,就在開發(fā)這兩個功能。為了簡化,假設(shè)昆特牌功能有函數(shù)playGwent, useMonsterCard,與女術(shù)士增進(jìn)感情功能有函數(shù)talk, fight等。那么,任何一個懂得規(guī)劃的開發(fā)者都會知道,不管使用什么編程語言,我們的代碼順序應(yīng)該是

// Gwent partvoid playGwent(Person person);

void useMonsterCard(Card monsterCard);

// ... many more functions

// Sorceress partvoid talk(Sorceress sorceress);

void fight(Sorceress sorceress);

// ... many more functions

而不是

void playGwent(Person person);

void talk(Sorceress sorceress);

// ... many more functionsvoid fight(Sorceress sorceress);

void useMonsterCard(Card monsterCard);

// ... many more functions

這樣把各種功能,毫不相關(guān)的函數(shù)交錯放置。只有通過合理地有序組織代碼,才能使代碼的開發(fā)和維護(hù)變得輕松一些。試想,如果一個開發(fā)者想維護(hù)我們的這個代碼,他想找到和女術(shù)士交談的函數(shù),與在整個項目代碼中一行一行找相比,那必然是直接在女術(shù)士相應(yīng)的代碼部分尋找更為輕松。

總而言之,模塊化實際上是一種開發(fā)分配、代碼組織的理念,就是將整個項目的代碼分成許多有獨立功能的模塊,將毫不相關(guān)的模塊分開。同時,對于沒有功能依賴的模塊,可以多位開發(fā)者并行開發(fā)。通過模塊化的措施,可以最大程度降低開發(fā)、維護(hù)的成本和時間。

多文件編程

將項目分為許多模塊,由開發(fā)者并行開發(fā),這是模塊化的理念。那么實際操作中,應(yīng)該如何貫徹呢?最直觀的想法,就是將不同的模塊歸屬到不同的文件、目錄下去。假如我們的簡陋版巫師三是用JavaScript寫的,那么如果和女術(shù)士增進(jìn)感情部分的代碼比較少,我們直接將這部分的代碼歸入到sorceress.js這個文件中;如果昆特牌部分的代碼比較多,放不到一個文件中,那就單獨設(shè)置一個目錄gwent, 在其中可能會有monster.js, play.js等多個文件。也就是說,我們的代碼結(jié)構(gòu)可能會是

project

├── main.js

├── sorceress.js

└── gwent

├── monster.js

└── play.js

這樣的層次結(jié)構(gòu)。此外,我們還可能會使用別人寫的庫的功能。假設(shè)我們有一個庫height用于計算跳落的傷害, 其代碼結(jié)構(gòu)是

height

├── damage.js

└── distance.js

將代碼分到不同的文件、目錄中,這不僅需要開發(fā)者遵守,編程語言也需要有相應(yīng)的多文件支持。也就是說,編程語言需要支持多文件編程。

此外,當(dāng)編程語言支持多文件編程時,還有一個問題引刃而解了——命名沖突。比如說,我們在昆特牌功能中有一個win函數(shù),表示比賽獲勝,而在和女術(shù)士增進(jìn)感情的功能中,也有一個win函數(shù),表示獲得其芳心。那么,最簡單的解決方案,就是一個函數(shù)叫winGwent, 一個函數(shù)叫winSorceress. 但是,通過編程語言對多文件的支持,或者說其對多模塊的支持,只要將其分屬于兩個模塊內(nèi),那么都叫win也就沒有關(guān)系了。

在討論大多數(shù)編程語言的多文件編程支持時,會涉及到三個概念:文件級別,目錄級別,以及庫級別。根據(jù)我們之前的討論,每個文件都包含一些單獨的功能。而對于那些功能有聯(lián)系的文件,會將其組織在同一個目錄下。而有的目錄具有的功能是一些輔助性的,可以復(fù)用的功能,所以有些目錄會被作為一個庫發(fā)布出去,供別的人使用,也就像我們這里的height庫一樣。在大多數(shù)編程語言的認(rèn)知中,一個文件被稱為一個「模塊」,一個目錄被稱為一個「包」。因此,一個項目本身也是一個包,它是由許多文件和包組成。而組成它的包又有下一層次的文件和包組成。而一個庫可能由許多包組成,也可能就是一個包。

對于庫而言,我會在后面關(guān)于包管理器的文章中專門提到,這里就先只討論模塊和包。在下面具體編程語言的討論中,模塊級別和包級別是我們剛才講的文件和目錄的概念,而非語言具體的名詞概念。

Python^1

Python是區(qū)分模塊級別和包級別的。在Python中,一個文件就是一個模塊,而一個目錄則是一個包。

一個目錄如果要聲明自己是一個包,則必須要在目錄中包含__init__.py文件。一個文件自動是一個模塊,不需要聲明。

如果要用Python完成我們的簡陋版巫師三,那么代碼結(jié)構(gòu)應(yīng)該為

project

├── main.py

├── sorceress.py

└── gwent

├── __init__.py

├── monster.py

└── play.py

在main.py中,需要按模塊引入。同時,引入別的庫的模塊和引入自己的模塊沒有差別。引入別的模塊的方法是

import sorceress

import gwent.monster

import gwent.play

import height.damage

Kotlin^2

使用Kotlin編寫的Android項目依然有模塊級別和包級別的概念。但是,從名詞術(shù)語的角度來看,Kotlin的模塊并不指單個文件,而是一個項目;Kotlin的包則指一個目錄。

一個目錄不需要聲明,自動是一個包。一個文件需要在開頭用package關(guān)鍵詞表明自己所屬的包。

如果要用Kotlin完成我們的簡陋版巫師三,那么代碼結(jié)構(gòu)應(yīng)該為

project

├── main.kt

├── sorceress.kt

└── gwent

├── monster.kt

└── play.kt

在main.kt及sorceress.kt的第一行,需要

package project

而在monster.kt和play.kt的第一行,則要

package project.gwent

在main.kt中,需要按包引入。一個文件會自動引入同一個包下的別的文件,如果要引入別的包或者庫(實際上也是包),則需要

import project.gwent

import height

JavaScript/TypeScript^3

自ECMAScript 2015之后,JavaScript有了模塊的概念。在JavaScript的視角下,目錄僅僅是目錄的作用,并沒有特殊的包的作用。因此,JavaScript只有模塊的概念。

一個文件如果要表明自己是一個模塊,則必須有export語句。

如果要用JavaScript或TypeScript完成我們的簡陋版巫師三,那么代碼結(jié)構(gòu)應(yīng)該為

project

├── main.js

├── sorceress.js

└── gwent

├── monster.js

└── play.js

對于main.js,引入別的模塊的方法是

import './sorceress.js';

import './gwent/monster.js';

import './gwent/play.js';

import 'path/to/height/damage.js';

值得注意的是,TypeScript在進(jìn)行import的時候,不需要帶擴(kuò)展名,也就是

import './sorceress';

import './gwent/monster';

import './gwent/play';

import 'path/to/height/damage';

Swift^5

Swift沒有模塊級別和包級別的概念,其「模塊」指的是庫的概念。

如果要用Swift完成我們的簡陋版巫師三,其代碼結(jié)構(gòu)與之前無異:

project

├── main.swift

├── sorceress.swift

└── gwent

├── monster.swift

└── play.swift

在同一個模塊下(也就是我們理解的在同一個庫內(nèi)),所有文件都是默認(rèn)導(dǎo)入的,我們不需要import來導(dǎo)入同項目下別的文件或目錄。但是,需要使用import語句導(dǎo)入別的庫,也就是Swift中的模塊:

import height

Rust^6

Rust的模塊系統(tǒng)和JavaScript相近,沒有包的概念。但其目錄和文件的地位是相同的,都是一個模塊,而模塊可以擁有子模塊。

具體而言,就是Rust把「模塊」和「包」的概念等同了,gwent目錄實際上就是gwent模塊,其下有子模塊monster和play.

一個Rust的文件自動是一個模塊,但需要在其父模塊中聲明。一個Rust的目錄必須包含mod.rs作為當(dāng)前模塊。

如果要用Rust來完成我們的簡陋版巫師三,其代碼結(jié)構(gòu)為

project

├── main.rs

├── sorceress.rs

└── gwent

├── mod.rs

├── monster.rs

└── play.rs

project

├── main.rs

├── sorceress.rs

├── gwent.rs

└── gwent

├── monster.rs

└── play.rs

在gwent/mod.rs或gwent.rs中,必須要有

mod monster;mod play;

來聲明其子模塊。

在main.rs中如果要想引入別的模塊,需要

mod sorceress;// declare sub modulemod gwent;// declare sub moduleusegwent::monster;// use sub moduleusegwent::play;// use sub moduleuseheight::damage;// use library module

由于Rust中目錄和文件的地位都是模塊,所以我們也可以同時use gwent和use gwent::monster.

訪問控制

使用模塊化編程后,會帶來更進(jìn)一步的好處,就是訪問控制。所謂訪問控制,就是誰能對誰干什么。一個訪問控制規(guī)則可以用一個三元組表示:主體,客體,訪問權(quán)限。我們的生活中常常會有訪問控制的存在,比如說,QQ空間中,僅好友可見,就是一種訪問控制規(guī)則,表明只有主體為我的好友的人,才能對客體——我的這條說說,進(jìn)行「讀」這一訪問權(quán)限。

在模塊化編程中,訪問控制就體現(xiàn)在我此時處在的代碼塊中,能否調(diào)用別的代碼塊中的函數(shù)。為什么要進(jìn)行訪問控制呢?這主要是為了貫徹封裝的理念。在一個庫中,有的函數(shù)也許只是作為庫內(nèi)的輔助函數(shù)使用,不暴露給外部,這時候就要對這些函數(shù)進(jìn)行訪問控制的保護(hù)。

最簡單的訪問控制,就是private和public. 被標(biāo)記為private的代碼只能被邏輯上處于同一個代碼塊的別的代碼調(diào)用,而被標(biāo)記為public的代碼卻能被所有的代碼訪問到。這一個思想作為基礎(chǔ),在此之上有許多的變種。

Rust^7

最符合邏輯的訪問控制操作是Rust. 它將模塊視作訪問控制的最小單元,其的原則只有一個:如果一個模塊能訪問某些代碼,那么它的所有子模塊都能訪問該代碼。根據(jù)這一宗旨,Rust的訪問控制實際上只分為兩種:pub(in path::to::module)在適當(dāng)?shù)拇a前加上這個限定符,代表當(dāng)前的代碼能夠被指定的模塊和其子模塊訪問。比如說以下的代碼:

```rust mod A { mod B { mod C { } } }

mod D { pub(in super::A::B) struct Foo { } } ```

那么,Foo這個結(jié)構(gòu)體本身位于D這個模塊,但它指定B模塊可以訪問自己,那么總共可以訪問Foo結(jié)構(gòu)體的模塊有B, C, D.

不加訪問控制限定符不加訪問控制限定符則默認(rèn)為私有。私有的代碼只能被當(dāng)前模塊和其子模塊訪問。比如說以下的代碼:

```rust mod A { mod B { struct Foo { } mod C { } } }

mod D { } ```

那么,能夠訪問到Foo這個結(jié)構(gòu)體的模塊只有B和C.

為了方便開發(fā)者,pub(in path)會有許多的語法糖,比如說pub(crate)代表在當(dāng)前crate內(nèi)能訪問,pub(super)代表父模塊能訪問,pub(self)代表只有本模塊能訪問,也就等同于不加訪問控制限定符。

Kotlin^8

Kotlin的訪問控制限定符則有4個:private, protected, internal和public. 由于Rust并不具有OOP的全部特性,所以其訪問控制可以通過簡單的修飾符達(dá)到完美的效果。但是,Kotlin等語言則是OOP更強(qiáng)的語言,所以其訪問控制的修飾符也更多了一些。

首先,我們來看每個符號的定義:private對于頂層代碼(即不寫在class內(nèi)部的代碼),是僅能由本文件內(nèi)部訪問

對于class內(nèi)的代碼,僅能由該類內(nèi)部訪問

protected對于class內(nèi)的代碼,僅能由該類及其子類訪問internal對于頂層代碼,能由整個Kotlin語義下的模塊(即我們眼中的庫級別)訪問

對于class內(nèi)的代碼,能由該Kotlin語義下的模塊內(nèi),能訪問該類的代碼訪問

public對于頂層代碼,能被所有代碼訪問

對于class內(nèi)的代碼,能由能訪問該類的代碼訪問

就像我們剛剛所說的,正是由于擁有了繼承關(guān)系,Kotlin的訪問限定符因此多了一個protected, 以及其他每個符號也多了class內(nèi)的含義。但是,其與Rust相比,僅能表示當(dāng)前文件內(nèi)(即private),或當(dāng)前庫級別內(nèi)(即internal)的訪問控制,不能做到任意包路徑的訪問控制。

Swift^9

Swift比Kotlin多了一個訪問控制限定符,共有5個:open, public, internal, fileprivate, private. 首先,我們還是先來看其定義:open能被所有代碼訪問,并且能被Swift語義下的模塊(即我們眼中的庫級別)外的類繼承

public能被所有代碼訪問internal能被Swift語義下的模塊內(nèi)的代碼訪問

fileprivate能被當(dāng)前文件內(nèi)的代碼訪問private僅能被當(dāng)前邏輯上的實體訪問

這樣看來,實際上和Kotlin是類似的,只不過open多了一個能不能被繼承的限定。

上述的三種語言使用的是傳統(tǒng)意義上的訪問控制,我們可以看到,Rust由于沒有OOP的完整特性,所以比較靈活。但雖然Kotlin和Swift不能指定路徑上的訪問控制,但在實際工程中,庫級別和文件級別的訪問控制實際已經(jīng)能夠勝任了。

JavaScript/TypeScript^10

JavaScript的訪問控制就比較粗糙了,但也是一個很有效的策略,它的思想就是:只要我export的,你就能用;只要我沒有export的,你就不能用。

我們之前講過,JavaScript和TypeScript中一個文件就是一個模塊。在JavaScript或TypeScript中,所有代碼都可以被同一模塊內(nèi)的其他代碼使用。但是,如果想讓別的模塊使用某些代碼,則必須將相應(yīng)的代碼導(dǎo)出去。比如說,我們有下面的代碼:

// foobar.ts

function foo() { }

export interface Foo { }

export function bar() { }

export default function baz() { }

那么,在別的模塊中,我們可以使用

import baz, { bar, Foo } from 'foobar'

來導(dǎo)入相應(yīng)的代碼。

Python

Python是最奇葩的一種訪問控制策略,它是少數(shù)的幾種通過變量名來控制訪問策略的語言。

首先是對于模塊來說,以_開頭的代碼不能被import^11. 比方說,我們有如下的一個模塊:

# foo.py

def bar():

print("bar")

def _baz():

print("barz")

那么,當(dāng)我們在別的模塊使用

from foo import *

這個語法的時候,并不會將_baz也一并導(dǎo)入。但是,要注意的是,如果我們單獨

from foo import _baz

是可以成功的。

其次,是對于類來說,以雙下劃線__開頭的代碼被認(rèn)為是不能被子類改寫的^12。比方說以官方文檔中的代碼為例:

class Mapping:

def __init__(self, iterable):

self.items_list = []

self.__update(iterable)

def update(self, iterable):

for item in iterable:

self.items_list.append(item)

__update = update # private copy of original update() method

class MappingSubclass(Mapping):

def update(self, keys, values):

# provides new signature for update()

# but does not break __init__()

for item in zip(keys, values):

self.items_list.append(item)

那么,當(dāng)我們實例化一個MappingSubclass的時候,即使子類提供了__update, 其構(gòu)造函數(shù)也不會調(diào)用子類的__update。

總結(jié)

以上是生活随笔為你收集整理的python质数列_现代化程序开发笔记(3)——多文件与模块的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。